Example
Import data
The following procedural example is based on documentation found at the rvest documentation site.
Import a webpage using read_html
results <- read_html("http://www.vondel.humanities.uva.nl/ecartico/persons/index.php?subtask=browse")
Results are in a list.
The results object is a list (R data type.) The items in the list correspond to the basic document structure of an HTML document…
Displaying the results object shows that the first item in the list is head. The second item is body. These items correspond to the basic structure of the HTML document type definition. In other words, the text, links, and HTML “stuff” were scraped from the web page. Specifically this stuff is found in the body element of the HTML document. This stuff is now stored in the body element of the restults list.
Contents of the results object
results
Example HTML
A simplified example HTML document
<HTML>
<HEAD>
<title>my example document</title>
</HEAD>
<BODY>
<h1>Hello World</h1>
<p>HTML is a tagging system known as the HypterText Markup Language</p>
</BODY>
</HTML>
Procedure
The basic workflow of web scraping is
Development
- Import raw HTML of a single target page (page detail, or “leaf”)
- Parse the HTML of the test page to gather the data you want
- In a web browser, manually browse and understand the site navigation of the scrape target (site navigation, or “branches”)
- Parse the site navigation and develop an interation plan
- Iterate: write code that implements iteration, i.e. automated page crawling
- Perform a dry run with a limited subset of the target web site
- check robots.txt, terms of use, and construct time pauses (to avoid DNS attacks)
Production
Iterate
- Crawl the site navigation (branches)
- Parse HTML for each detail page (leaves)
Parse the nodes of the HTML tree.
A web page is composed of HTML and prose. The web document, just as the web site, has a hierarchical structure. Web scraping means parsing these structures to gather needed information.
The first step is to start with a single target single document (i.e. a web page or a leaf of the site). In this case, the document we want to parse is the summary navigation page consisting of the first 50 names listed alphabetically in this web site. The goal is to parse the HTML source of that web page (i.e. document) by traversing the nodes of the document’s HTML structure. In other words, we want to mine text and data from the body section of the results list. In this example I’ll gather all the HTML within the <li> tags.
li stands for “list item”. You can learn more about the li tag structure from HTML documentation.
Goal: Briefly…
- limit the results to the list item nodes of the
body of the HTML document tree. This is done with the html_nodes() function.html_nodes("li")
- Use the
html_text() function to parse the text of the HTML list item (i.e. the <li> tag)
For Example
in an HTML document that has tagging such as this:
<li><a href="/ecartico/persons/17296">Anna Aaltse (1715 - 1738)</a></li>
I want to gather the text within the <li> tag: Anna Aaltse (1715 - 1738)
CODE
Using the html_nodes() and html_text() functions, I can retrieve all the text within <li></li> tags.
names <- results %>%
html_nodes("li") %>%
html_text()
names
[1] "About ECARTICO" "+ Analysis"
[3] "+ Search & browse" "+ Impressum"
[5] "Hillebrand Boudewynsz. van der Aa (1661 - 1717)" "Boudewijn Pietersz van der Aa (? - ?)"
[7] "Pieter Boudewijnsz. van der Aa (1659 - 1733)" "Boudewyn van der Aa (1672 - ?)"
[9] "Machtelt van der Aa (? - ?)" "Claas van der Aa I (? - ?)"
[11] "Claas van der Aa II (? - ?)" "Willem van der Aa (? - ?)"
[13] "Hans von Aachen, alias: Hans / Johann van Aken (1552 - 1615)" "Jacobus van Aaken (? - ?)"
[15] "Justus van Aaken (? - ?)" "Johannes Aalmis, alias: Jan (1714 - 1799)"
[17] "Johan Bartholomeus Aalmis (1723 - 1786)" "Maria van Aalst (1639 - 1664)"
[19] "Anna Aalst (? - ?)" "Anna Aaltse (1715 - 1738)"
[21] "Allart Aaltsz (1665 - 1748)" "Geertruy Aaltsz (? - 1732)"
[23] "Maria Aaltsz (? - 1746)" "Catharina Aaltsz (? - 1727)"
[25] "Nikolaas van Aaltwijk (1692 - 1727)" "Maria Aams (1711 - 1774)"
[27] "Jacobus Aams (1680 - ?)" "Jan Govertsz. van der Aar (1544 - 1612)"
[29] "Anna van der Aar (1576 - 1656)" "Janneke Jans van Aarden (1609 - 1651)"
[31] "Abraham van Aardenberg (1672 - 1717)" "Willem Aardenhout I (? - ?)"
[33] "Margrietje Aarlincx (1637 - 1690)" "Dirck van Aart (1680 - 1737)"
[35] "Jonas Abarbanel, alias: Abravanel; Abrabanel (? - 1667)" "Josephus Abarbanel (? - ?)"
[37] "Esther Abarbanel (? - ?)" "Rachel Abarbanel (? - ?)"
[39] "Lea Abarbanel (1691 - ?)" "Isaac Abarbanel (1637 - 1723)"
[41] "Damiana Abarca (? - 1630)" "Bartholomeus Abba (1641 - 1684)"
[43] "Cornelis Dirksz. Abba (1604 - 1675)" "Clara Abba (1631 - 1671)"
[45] "Aerlant Abbas (1606 - 1696)" "Matheus Jansz Abbas, alias: Diercsz (1569 - ?)"
[47] "Hendrik Abbé, alias: Enrico Abè (1639 - 1677)" "Claude Abbé, alias: Glaude (? - 1653)"
[49] "Simon Jan Pontenz. Abbe (1467 - 1549)" "Simon IJsbrandz. Abbe (? - ?)"
[51] "Ysbrandt Simonsz. Abbe (? - 1559)" "Maximiliaen l' Abbé, alias: Labbé (? - 1675)"
[53] "Marten Simonsz. Abbe genaamd Schuyt (? - 1592)" "Daniël Abbeloos (ca. 1635 - 1677)"
Parse the HTML attributes of an HTML tag…
Beyond the text you may also want attributes of HTML tags. To mine the URL of a hypertext link <a href="URL"></a>, within a list item, you need to parse the HREF argument of an anchor tag. If you’re new to web scraping, you’re going to need to learn something about HTML tags, such as the anchor tag.
For Example
in an HTML document that has tagging such as this:
<a href="https://search.com">Example Link</a>
I want to gather the value of the href attribute within the anchor tag: https://search.com
CODE
Using the html_nodes() and html_attr() functions, I can retrieve all the attribute values within <li><a></a></li> tags.
url <- results %>%
html_nodes("li a") %>%
html_attr("href")
url
[1] "../" "../analysis/" "../persons/" "../impressum/" "/ecartico/persons/414"
[6] "/ecartico/persons/10566" "/ecartico/persons/10567" "/ecartico/persons/10568" "/ecartico/persons/27132" "/ecartico/persons/33780"
[11] "/ecartico/persons/33781" "/ecartico/persons/33782" "/ecartico/persons/9203" "/ecartico/persons/33052" "/ecartico/persons/33053"
[16] "/ecartico/persons/43671" "/ecartico/persons/43672" "/ecartico/persons/30222" "/ecartico/persons/38845" "/ecartico/persons/17296"
[21] "/ecartico/persons/38518" "/ecartico/persons/38523" "/ecartico/persons/38524" "/ecartico/persons/38525" "/ecartico/persons/43337"
[26] "/ecartico/persons/42619" "/ecartico/persons/42620" "/ecartico/persons/41311" "/ecartico/persons/49902" "/ecartico/persons/20653"
[31] "/ecartico/persons/47922" "/ecartico/persons/33783" "/ecartico/persons/42051" "/ecartico/persons/28921" "/ecartico/persons/37887"
[36] "/ecartico/persons/37890" "/ecartico/persons/37892" "/ecartico/persons/38352" "/ecartico/persons/42876" "/ecartico/persons/42881"
[41] "/ecartico/persons/22859" "/ecartico/persons/52962" "/ecartico/persons/52963" "/ecartico/persons/52965" "/ecartico/persons/17297"
[46] "/ecartico/persons/55241" "/ecartico/persons/416" "/ecartico/persons/11593" "/ecartico/persons/41739" "/ecartico/persons/41742"
[51] "/ecartico/persons/41743" "/ecartico/persons/52649" "/ecartico/persons/41738" "/ecartico/persons/417"
Note that the above links, or hrefs, are relative URL paths. I still need the domain name for the web server http://www.vondel.humanities.uva.nl.
Systematize
Above I created two vectors, one vector, names, is the html_text that I parsed from the <li> tags within the <body> of the HTML document. The other vector, url, is a vector of the values of the href attribute of the anchor <a> tags.
Placing both vectors into a tibble makes manipulation easier when using tidyverse techniques.
Goal
I want to develop a systematic workflow that builds a tibble consisting of each of the 50 names listed in the summary results object retrieved by read_html() performed on this page. Of course, mining and parsing the data is just the beginning. Data cleaning is a vital and constant aspect of web scraping.
Build Tibble
Using vectors from parsing functions above….
results_df <- tibble(names, url)
results_df
From above we have links in a url vector, and target names in a names vector, for fifty names from the target website we want to crawl. Of course you also want to parse data for each person in the database. To do this we need to read (i.e. read_html()) the HTML for each relevant url in the results_df tibble. Below is an example of how to systematize the workflow. To do that, we’ll make a results tibble, results_df. But first, more data cleaning…
Create some new variables with mutate. Build a full URL from the relative URL path (i.e. the url vector) and the domain or base URL of the target site. Since we scraped the relative URL path, we have to construct a full URL.
urls_to_crawl_df <- results_df %>%
# mutate(base_url = "") %>%
mutate(full_url = glue::glue("http://www.vondel.humanities.uva.nl{url}")) %>%
mutate(full_url = str_replace_all(full_url, "\\.\\.", "")) %>%
select(full_url)
urls_to_crawl_df #
As you can see, above, it’s really helpful to know about Tidyverse text manipulation, specifically mutate, glue, and pattern matching and regex using the stringr package.
Operationalize the workflow
To operationalize this part of the workflow, you want to iterate over the vector full_url found in the urls_to_crawl_df tibble. Then read_html for each name that interest you. Remember that only 50 of the 54 rows in the resutls_df tibble are target names to crawl. So, really, you still have some data wrangling to do. How can you eliminate the four rows in the results_df tibble that are not targets (i.e. names)? Somewhere, below, I’ll also show you how to exclude the four rows of unnecessary/unhelpful information.
Navigation and crawling
Meanwhile, we still have the goal to systematically crawl the site’s navigation links for each of the 20+ summary results pages, each of which consists of fifty names. Remember, each summary results page also has links to web site navigation. Navigation links are the key to crawling through the other summary results pages. You will need to write code that crawls through the navigation links, then read_html() for each of the fifty name/urls in that particular summary results page.
Therefore, one crawling goal is to build up a tibble that contains URLS for each summary results page. So far we only have links to fifty names from the first summary results page. We don’t have the URLs for each of the 20+ navigation pages. How do we get those? First find the pattern for the links to the navigation pages. Let’s get the navigation link to the second summary results page.
http://www.vondel.humanities.uva.nl/ecartico/persons/index.php?subtask=browse&field=surname&strtchar=A&page=2
After some investigation of the the HTML source, I see the only difference bewteen navigation URLS is the value of the URL argument page=. Eventually we need to construct a tibble with all the URLs for each of the navigation pages. But first, let’s learn a bit more about HTML parsing.
Deconstructing a single target page and site design
So far we’ve used rvest functions and learned a little HTML. You also need to know about CSS (Cascading Style Sheets).
You can follow the link above for a definition of CSS. As a quickstart appraoch, I recommend playing this CSS game as a fun and quick way to learn just enough CSS. I completed the first 15 levels of the game; that was enough to get a good foundation with CSS. Depending on the complexity of the HTML and CSS, you may need to know a little more or a little less. But you need to know something about HTML and CSS.
CSS will help us subset the HTML for a single target page. I didn’t mention it before but, you often need to view the source of the HTML; use the chrome browser to inspect elements of pages in a web browser; and use the chrome browser extension, selector gadget, to better understand the HTML and CSS tagging and structure.
This is key. When web scraping, you are effectively reverse engineering the web site. To reverse engineer it, you must have a solid understanding of the target site’s structure, the site navigation, HTML, and CSS. Knowledge of the site structure is independent of R, rvest, lists, and purrr.
Anyway, an example of a CSS element in the results page is ‘class’ attribute of the <div> tag. In the case below we also have a class value of “subnav”. Viewing the source HTML of one summary results page will show the <div> tags with the CSS class element.
For Example
Use is html_nodes() with html_text(), and html_attr() to parse the anchor tag nodes, <a>, found within the <div> tags which contain the class="subnav" attribute.
<div class = "subnav">
...
<a href="/ecartico/persons/index.php?subtask=browse&field=surname&strtchar=A&page=3">[101-150]</a>
...
</div>
CODE
Parse the text of the navigation bar.
results %>% #html_nodes("div.subnav")
html_nodes("div.subnav a") %>%
html_text()
[1] "Search" "Advanced search" "Browse" "Selections" "[51-100]" "[101-150]" "[151-200]"
[8] "[201-250]" "[1051-1099]" "[51-100]" "[101-150]" "[151-200]" "[201-250]" "[1051-1099]"
Parse the HTML href attribute to get the URL.
navigation <- results %>%
html_nodes("div.subnav a") %>%
html_attr("href")
navigation
[1] "/ecartico/persons/index.php"
[2] "/ecartico/persons/index.php?subtask=perssearch"
[3] "/ecartico/persons/index.php?subtask=browse"
[4] "/ecartico/persons/index.php?subtask=selections"
[5] "/ecartico/persons/index.php?subtask=browse&field=surname&strtchar=A&page=2"
[6] "/ecartico/persons/index.php?subtask=browse&field=surname&strtchar=A&page=3"
[7] "/ecartico/persons/index.php?subtask=browse&field=surname&strtchar=A&page=4"
[8] "/ecartico/persons/index.php?subtask=browse&field=surname&strtchar=A&page=5"
[9] "/ecartico/persons/index.php?subtask=browse&field=surname&strtchar=A&page=22"
[10] "/ecartico/persons/index.php?subtask=browse&field=surname&strtchar=A&page=2"
[11] "/ecartico/persons/index.php?subtask=browse&field=surname&strtchar=A&page=3"
[12] "/ecartico/persons/index.php?subtask=browse&field=surname&strtchar=A&page=4"
[13] "/ecartico/persons/index.php?subtask=browse&field=surname&strtchar=A&page=5"
[14] "/ecartico/persons/index.php?subtask=browse&field=surname&strtchar=A&page=22"
Crawl summary results pages
We want to gain a clear sense of the predictable navigation structure of our target site, and the navigation URLs, so that we can crawl through each page of the target site. Again, the task before us is to do this crawling systematically. Let’s put the navigation object into a tibble data-frame called nav_df.
nav_df <- tibble(navigation)
nav_df
In the above tibble, I see I need to clean the nav_df$navigation vector so that only the unique URLs for each 50-result summary page remain. One problem is there are only 14 rows and a lot of redundancy and many missing summary page URLS. One reason I know this is that I have a link for page 2, 3, 4, 5, and 22; but nothing, no URLs for pages between pages 6 and 21. This mirrors what we see when we view the actual target web page in a web browser. That is, I have links for results [1-50], [51-100], [100-150], but nothing for [251-260] (i.e. page 6), etc. The target page is composed of HTML and CSS and comprises what we have in our imported results object, above. To confirm my investigation, I’ll use a web browser to view the HTML source of one of the summary pages. The unparsed HTML is what you have in results object.
What I see in the view source or the nav_df$navigation vector is that each navigation URL is the same, except for the difference in the page= element found at the end of the URL argument.
Mini review summary
We can see that the navigation URL pattern is predictable. We can construct a full URL for each summary results target page. We can make a tibble with all these links, one row for each URL to each navigation section of each summary results page of the target site. Once we have this, we can write a script to systematically browse, or crawl, through the target site just as if we manually mouse-clicked each link in a web browser. We will use tidyverse packages to crawl through the site and rvest::html_read() to import the HTML. After we crawl each page, we’ll parse the target HTML data.
Develop a plan
Goal make a tibble with all the navigation links
Let’s start by importing the HTML of a single summary results page. Then I’ll make a tibble of the navigation links found within the gathered and relevant <div> HTML tags, i.e. the div tags with the subnav CSS class. We did that with the nav_df tibble.
After we gather those navigation links in nav_df$navigation, we will wrangle and expand the vector to include only the relevant navigation URLs. But, why did we have duplicate navigation URLS in that vector? Look at the summary page in a web browser. See any duplication? If not, look closer.
Below, use dplyr::distinct() and stringr::string_extract() among other tidyverse functions to wrangle a tibble of relevant links.
nav_df <- nav_df %>%
filter(str_detect(navigation, "&page=")) %>%
distinct(navigation) %>%
mutate(page_no = str_extract(navigation, "\\d+$")) %>%
mutate(page_no = as.numeric(page_no))
nav_df
There’s some fancy regex pattern matching taking place in the data wrangling code chunk, above. Remember how I said you need to learn about pattern matching and the stringr library? Yup.
Anyway…
Now all we need to do is expand the sequence of pages. (Hint: tidyr::expand() )
The maximum number of summary pages in the navigation$page_no variable is 22. This should mean the maximum number of URLs to summary results pages will be roughly 22. Regardless of the total number of target names/pages, our task is to build a tibble with a URL for each summary results page, i.e. pages 1 thorough 22. IF we have a link to each sumamry results page, then can we get a link for each of the fifty people listed on each of the summary result pages.
Honestly, building this list of navigation URLs takes some effort in R, especially if you’re new to R. So, maybe, there’s an easier way. It might be easier to build the range of summary page URLs in Excel, then import the excel file (or CSV file) of URLs into R for crawling via rvest. But I want a reproducible, code-based approach.
See example code, below, for a reproducible example. With the next code chunk, you can use Tidyverse techniques and build a tibble of urls to iterate over. In this case, the important reproducible step use stringr::str_extract() to find and match the URL pattern.
nav_df <- nav_df %>%
mutate(navigation = str_extract(navigation, ".*(?=\\=\\d+$)")) %>%
mutate(page_no = as.integer(str_replace(page_no, "^2$", "1"))) %>%
expand(navigation, page_no = full_seq(page_no, 1)) %>%
transmute(url = glue::glue("http://www.vondel.humanities.uva.nl{navigation}={page_no}"))
# the regular expression '.*(?=\\=\\d+$)' can be read as:
# capture/find data found BEFORE '(?=<<pattern>>)' the pattern.
# The pattern is =<digit><digit>,
# the '=' sign has to be "escaped" '\\='.
# The precise regex pattern syntax has the equal sign followed by a digit '\\d'.
# multiple digits '+', i.e. '\\d+'
# Since the digits are found at the end of the value of the variable, use an anchor
# To anchor the end of the regex pattern, use the '$' regex anchor notation.
# Remember, we are matching everything before the pattern. Match with the wildcard: '.*'
# Honestly, there's no substitute for knowing a bit about regex when you're coding.
#
# See the "work with strings" cheatsheet. https://rstudio.com/resources/cheatsheets/
nav_df
Iterate
Use purrr::map instead of ‘for’ loops. Because purrr is the R/Tidyverse way. ‘For’ loops are fine, but invest some time learning purrr and you’ll be better off. Still, there’s no wrong way to iterate as long as you get the right answer. So, do what works. Below is the Tidyverse/Purrr way….
Now that I have a full list of navigation URLs, each of which represents a web page that has a summary of 50 names/links. My next task is to read the HTML of each URL representing a target-name. By reading the URL (importing the HTML) for each target name, I will then have HTML for each individual target person in the database. Of course, I still, then, have to read and parse the HTML of those target-name pages, but I can do that. The scraping (crawling + parsing) works when I have a URL per target person. Having a URL for each target person means I can systematically scrape the web site. In other words, I crawl the summary navigation to construct a URL for each summary page. Then I import HTML for each summary page to get a URL to each person’s page. Then I import each person’s page and parse the HTML for each person’s record.
But, back to the current task: import the HTML for each summary results page of 50 records…
Note: that, below, I introduce a pause (Sys.sleep()) in front of each read_html() function. This is a common technique for well behaved web scraping. Pausing before each read_html function, avoids overwhelming my target’s server/network infrastructure. If I overwhelm the target server, the server host-people may consider me a DNS attack. If they think I’m a DNS attacker, they might choose to block my computer from crawling their site. If that happens, I’m up a creek. I don’t want that. I want my script to be a well behaved bot-crawler.
Speaking of being a good and honorable scraper-citizen, did I browse the robots.txt page for the site? Did I check the site for a Terms of Service page? Did I look to see if there were any written prohibitions against web crawling, systematic downloading, copyright, or licensing restrictions? I did and you should too. As of this writing, there do not appear to be any restrictions for this site. You should perform these types of good-scraping hygiene steps for every site you want to scrape!
Note: Below, for development purposes, I limited my crawling to 3 results links: my_url_df$url[1:3]. Be conservative during your code development to avoid appearing as a DNS attacker. Later, when you are ready to crawl your whole target site, you’ll want to remove such limits (i.e. [1:3].) But for now, do everyone a favor and try not to be over confident. Stay in the kiddie pool. Do your development work until you are sure you’re not accidentally unleashing a malicious or poorly constructed web crawler.
Note: Below, I am keeping the original target URL variable, summary_url, for later reference. This way I will have a record of which parsed data results came from which URL web page.
Note: Below, the final result is a tibble with a vector, summary_url, and an associated column of HTML results, each result is stored as a nested R list. That is, a column of data types that are all “lists”, aka a “list column”. Personally I find lists to be a pain. I prefer working with tibbles (aka data frames.). But lists appear often in R data wrangling, especially when scraping with rvest. The more you work with lists, the more you come to tolerate lists for the flexible data type that they are. Anyway, if I were to look at only the first row of results from the html_results column, nav_results_list$html_results[1], I would find a list of the raw HTML from the first summary results page imported via read_html().
Recapping: This is testing. I have three URLs (html_reults[1:3]), one for each of the first three navigation summary pages. Each summary page will contain the raw HTML for 50 names. I will read_html each link, waiting 2 seconds between each read_html.
nav_results_list <- tibble(
html_results = map(nav_df$url[1:3],
~ {
#url[1:3] - limiting to the first three summary results pages (each page = 50 results)
Sys.sleep(2)
# DO THIS! sleep 2 will pause 2 seconds between server requests to avoid being identified and potentially blocked by my target web server that might see my crawling bot as a DNS attack.
.x %>%
read_html()
}),
summary_url = nav_df$url[1:3]
)
nav_results_list
Now I have three rows of lists, each list with 50 links, in a tibble. Each link leads to a target name that I can eventually read_html to gather the raw HTML of that target name.
But first, I want to expand the three lists so I have a single tibble of 150 URLs to target names. Using purrr (map()), I can iterate over the results lists, parsing the HTML nodes with html_attr() and html_text(). It is convenient to keep this parsed data in a tibble. The results will be nested lists within a tibble. When I expand the nested list with the unnest() function, I then have a single tibble with 150 URLs and 150 names, one row for each target name.
results_by_page <- tibble(summary_url = nav_results_list$summary_url,
url =
map(nav_results_list$html_results,
~ .x %>%
html_nodes("ul li a") %>%
html_attr("href")),
name =
map(nav_results_list$html_results,
~ .x %>%
html_nodes("ul li a") %>%
html_text()
)
)
results_by_page
results_by_page %>%
unnest(cols = c(url, name)) %>%
filter(!str_detect(name, "ECARTICO")) %>%
filter(!str_detect(name, "^\\+"))
NA
Now I can iterate over each one of the URLs to the target names. Then I can parse the raw HTML for each target name page. When I follow the links for each name, I have the raw HTML of each person, in lists, ready to be parsed with the html_nodes, html_text, and html_attr functions.
LS0tDQp0aXRsZTogInJ2ZXN0IHR1dG9yaWFsIGRlbW9uc3RyYXRpb24iDQphdXRob3I6ICJKb2huIExpdHRsZSINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCmxpY2Vuc2U6ICJDQyBCWS1OQyIgIA0KQ3JlYXRpdmUgQ29tbW9uczogIEF0dHJpYnV0aW9uLU5vbkNvbW1lcmljYWwgIA0KaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLzQuMC8gIA0KDQoNCiMjIExvYWQgbGlicmFyeSBwYWNrYWdlcw0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJ2ZXN0KQ0KbGlicmFyeShodG1sdG9vbHMpDQp0YWdMaXN0KHJtYXJrZG93bjo6aHRtbF9kZXBlbmRlbmN5X2ZvbnRfYXdlc29tZSgpKQ0KYGBgDQoNCiMjIEV4YW1wbGUNCg0KIyMjIEltcG9ydCBkYXRhDQoNClRoZSBmb2xsb3dpbmcgcHJvY2VkdXJhbCBleGFtcGxlIGlzIGJhc2VkIG9uIGRvY3VtZW50YXRpb24gZm91bmQgYXQgdGhlIFtgcnZlc3RgXShodHRwczovL3J2ZXN0LnRpZHl2ZXJzZS5vcmcvKSBkb2N1bWVudGF0aW9uIHNpdGUuDQoNCiMjIyMgSW1wb3J0IGEgd2VicGFnZSB1c2luZyBgcmVhZF9odG1sYA0KDQpgYGB7cn0NCnJlc3VsdHMgPC0gcmVhZF9odG1sKCJodHRwOi8vd3d3LnZvbmRlbC5odW1hbml0aWVzLnV2YS5ubC9lY2FydGljby9wZXJzb25zL2luZGV4LnBocD9zdWJ0YXNrPWJyb3dzZSIpDQpgYGANCg0KIyMjIFJlc3VsdHMgYXJlIGluIGEgbGlzdC4NCg0KVGhlIGByZXN1bHRzYCBvYmplY3QgaXMgYSBfbGlzdF8gKFIgZGF0YSB0eXBlLikgIFRoZSBpdGVtcyBpbiB0aGUgbGlzdCBjb3JyZXNwb25kIHRvIHRoZSBiYXNpYyBkb2N1bWVudCBzdHJ1Y3R1cmUgb2YgYW4gSFRNTCBkb2N1bWVudC4uLg0KDQpEaXNwbGF5aW5nIHRoZSBgcmVzdWx0c2Agb2JqZWN0IHNob3dzIHRoYXQgdGhlIGZpcnN0IGl0ZW0gaW4gdGhlIF9saXN0XyBpcyBgaGVhZGAuICBUaGUgc2Vjb25kIGl0ZW0gaXMgYGJvZHlgLiAgVGhlc2UgaXRlbXMgY29ycmVzcG9uZCB0byB0aGUgYmFzaWMgc3RydWN0dXJlIG9mIHRoZSBIVE1MIGRvY3VtZW50IHR5cGUgZGVmaW5pdGlvbi4gIEluIG90aGVyIHdvcmRzLCB0aGUgX3RleHQsIGxpbmtzXywgYW5kIEhUTUwgInN0dWZmIiB3ZXJlIHNjcmFwZWQgZnJvbSB0aGUgd2ViIHBhZ2UuICBTcGVjaWZpY2FsbHkgdGhpcyBzdHVmZiBpcyBmb3VuZCBpbiB0aGUgX2JvZHlfIGVsZW1lbnQgb2YgdGhlIEhUTUwgZG9jdW1lbnQuIFRoaXMgc3R1ZmYgaXMgbm93IHN0b3JlZCBpbiB0aGUgYGJvZHlgIGVsZW1lbnQgb2YgdGhlIGByZXN0dWx0c2AgX2xpc3RfLg0KDQoqKkNvbnRlbnRzIG9mIHRoZSBgcmVzdWx0c2Agb2JqZWN0KioNCg0KYGBge3J9DQpyZXN1bHRzDQpgYGANCg0KKipFeGFtcGxlIEhUTUwqKg0KDQpBIHNpbXBsaWZpZWQgZXhhbXBsZSBIVE1MIGRvY3VtZW50DQoNCmBgYGh0bWwNCjxIVE1MPg0KICA8SEVBRD4NCiAgICA8dGl0bGU+bXkgZXhhbXBsZSBkb2N1bWVudDwvdGl0bGU+DQogIDwvSEVBRD4NCiAgPEJPRFk+DQogICAgPGgxPkhlbGxvIFdvcmxkPC9oMT4NCiAgICA8cD5IVE1MIGlzIGEgdGFnZ2luZyBzeXN0ZW0ga25vd24gYXMgdGhlIEh5cHRlclRleHQgTWFya3VwIExhbmd1YWdlPC9wPg0KICA8L0JPRFk+DQo8L0hUTUw+DQpgYGANCg0KDQoNCiMjIyBQcm9jZWR1cmUNCg0KVGhlIGJhc2ljIHdvcmtmbG93IG9mIHdlYiBzY3JhcGluZyBpcw0KDQoxLiBEZXZlbG9wbWVudA0KDQogICAgLSBJbXBvcnQgcmF3IEhUTUwgb2YgYSBzaW5nbGUgdGFyZ2V0IHBhZ2UgKHBhZ2UgZGV0YWlsLCBvciAibGVhZiIpDQogICAgLSBfUGFyc2VfIHRoZSBIVE1MIG9mIHRoZSB0ZXN0IHBhZ2UgdG8gZ2F0aGVyIHRoZSBkYXRhIHlvdSB3YW50DQogICAgLSBJbiBhIHdlYiBicm93c2VyLCBtYW51YWxseSBicm93c2UgYW5kIHVuZGVyc3RhbmQgdGhlIHNpdGUgbmF2aWdhdGlvbiBvZiB0aGUgc2NyYXBlIHRhcmdldCAoc2l0ZSBuYXZpZ2F0aW9uLCBvciAiYnJhbmNoZXMiKQ0KICAgIC0gX1BhcnNlXyB0aGUgc2l0ZSBuYXZpZ2F0aW9uIGFuZCBkZXZlbG9wIGFuIGludGVyYXRpb24gcGxhbg0KICAgIC0gSXRlcmF0ZTogIHdyaXRlIGNvZGUgdGhhdCBpbXBsZW1lbnRzIGl0ZXJhdGlvbiwgaS5lLiBhdXRvbWF0ZWQgcGFnZSBfY3Jhd2xpbmdfIA0KICAgIC0gUGVyZm9ybSBhIGRyeSBydW4gd2l0aCBhIGxpbWl0ZWQgc3Vic2V0IG9mIHRoZSB0YXJnZXQgd2ViIHNpdGUNCiAgICAtIGNoZWNrIHJvYm90cy50eHQsIHRlcm1zIG9mIHVzZSwgYW5kIGNvbnN0cnVjdCB0aW1lIHBhdXNlcyAodG8gYXZvaWQgRE5TIGF0dGFja3MpDQoNCjIuIFByb2R1Y3Rpb24NCg0KICAgIC0gSXRlcmF0ZQ0KICAgIA0KICAgICAgICBhLiAqKkNyYXdsKiogdGhlIHNpdGUgbmF2aWdhdGlvbiAoYnJhbmNoZXMpDQogICAgICAgIGIuICoqUGFyc2UqKiBIVE1MIGZvciBlYWNoIGRldGFpbCBwYWdlIChsZWF2ZXMpDQogICAgICAgIA0KIyMjIFBhcnNlIHRoZSBub2RlcyBvZiB0aGUgSFRNTCB0cmVlLiAgDQoNCkEgd2ViIHBhZ2UgaXMgY29tcG9zZWQgb2YgSFRNTCBhbmQgcHJvc2UuICBUaGUgd2ViIGRvY3VtZW50LCBqdXN0IGFzIHRoZSB3ZWIgc2l0ZSwgaGFzIGEgaGllcmFyY2hpY2FsIHN0cnVjdHVyZS4gIFdlYiBzY3JhcGluZyBtZWFucyBwYXJzaW5nIHRoZXNlIHN0cnVjdHVyZXMgdG8gZ2F0aGVyIG5lZWRlZCBpbmZvcm1hdGlvbi4NCg0KVGhlIGZpcnN0IHN0ZXAgaXMgdG8gc3RhcnQgd2l0aCBhIHNpbmdsZSB0YXJnZXQgc2luZ2xlIGRvY3VtZW50IChpLmUuIGEgd2ViIHBhZ2Ugb3IgYSBsZWFmIG9mIHRoZSBzaXRlKS4gIEluIHRoaXMgY2FzZSwgdGhlIGRvY3VtZW50IHdlIHdhbnQgdG8gcGFyc2UgaXMgdGhlIFtzdW1tYXJ5IG5hdmlnYXRpb24gcGFnZV0oaHR0cDovL3d3dy52b25kZWwuaHVtYW5pdGllcy51dmEubmwvZWNhcnRpY28vcGVyc29ucy9pbmRleC5waHA/c3VidGFzaz1icm93c2UpIGNvbnNpc3Rpbmcgb2YgdGhlIGZpcnN0IDUwIG5hbWVzIGxpc3RlZCBhbHBoYWJldGljYWxseSBpbiB0aGlzIHdlYiBzaXRlLiBUaGUgZ29hbCBpcyB0byBwYXJzZSB0aGUgSFRNTCBzb3VyY2Ugb2YgdGhhdCB3ZWIgcGFnZSAoaS5lLiBkb2N1bWVudCkgYnkgdHJhdmVyc2luZyB0aGUgbm9kZXMgb2YgdGhlIGRvY3VtZW50J3MgSFRNTCBzdHJ1Y3R1cmUuICBJbiBvdGhlciB3b3Jkcywgd2Ugd2FudCB0byBtaW5lIHRleHQgYW5kIGRhdGEgZnJvbSB0aGUgYGJvZHlgIHNlY3Rpb24gb2YgdGhlIGByZXN1bHRzYCBfbGlzdF8uICBJbiB0aGlzIGV4YW1wbGUgSSdsbCBnYXRoZXIgYWxsIHRoZSBIVE1MIHdpdGhpbiB0aGUgYDxsaT5gIHRhZ3MuICANCg0KYGxpYCBzdGFuZHMgZm9yICJsaXN0IGl0ZW0iLiAgWW91IGNhbiBsZWFybiBtb3JlIGFib3V0IHRoZSBfbGlfIHRhZyBzdHJ1Y3R1cmUgZnJvbSBbSFRNTCBkb2N1bWVudGF0aW9uXShodHRwczovL3d3dy53M3NjaG9vbHMuY29tL1RBR1MvdGFnX2xpLmFzcCkuDQoNCioqR29hbDogIEJyaWVmbHkuLi4qKg0KDQotIGxpbWl0IHRoZSByZXN1bHRzIHRvIHRoZSBfbGlzdCBpdGVtXyAqKm5vZGVzKiogb2YgdGhlIGBib2R5YCBvZiB0aGUgSFRNTCBkb2N1bWVudCB0cmVlLiAgVGhpcyBpcyBkb25lIHdpdGggdGhlIGBodG1sX25vZGVzKClgIGZ1bmN0aW9uLmBodG1sX25vZGVzKCJsaSIpYA0KLSBVc2UgdGhlIGBodG1sX3RleHQoKWAgZnVuY3Rpb24gdG8gcGFyc2UgdGhlIHRleHQgb2YgdGhlIEhUTUwgX2xpc3QgaXRlbV8gKGkuZS4gdGhlIGA8bGk+YCB0YWcpDQoNCioqRm9yIEV4YW1wbGUqKg0KDQppbiBhbiBIVE1MIGRvY3VtZW50IHRoYXQgaGFzIHRhZ2dpbmcgc3VjaCBhcyB0aGlzOg0KDQpgYGBodG1sDQo8bGk+PGEgaHJlZj0iL2VjYXJ0aWNvL3BlcnNvbnMvMTcyOTYiPkFubmEgQWFsdHNlICgxNzE1IC0gMTczOCk8L2E+PC9saT4NCmBgYA0KDQpJIHdhbnQgdG8gZ2F0aGVyIHRoZSB0ZXh0IHdpdGhpbiB0aGUgYDxsaT5gIHRhZzogICoqQW5uYSBBYWx0c2UgKDE3MTUgLSAxNzM4KSoqDQoNCioqQ09ERSoqDQoNClVzaW5nIHRoZSBgaHRtbF9ub2RlcygpYCBhbmQgYGh0bWxfdGV4dCgpYCBmdW5jdGlvbnMsIEkgY2FuIHJldHJpZXZlIGFsbCB0aGUgdGV4dCB3aXRoaW4gYDxsaT48L2xpPmAgdGFncy4NCg0KYGBge3J9DQpuYW1lcyA8LSByZXN1bHRzICU+JSANCiAgaHRtbF9ub2RlcygibGkiKSAlPiUgDQogIGh0bWxfdGV4dCgpDQoNCm5hbWVzDQpgYGANCg0KIyMjIFBhcnNlIHRoZSBIVE1MIGF0dHJpYnV0ZXMgb2YgYW4gSFRNTCB0YWcuLi4NCg0KQmV5b25kIHRoZSB0ZXh0IHlvdSBtYXkgYWxzbyB3YW50IGF0dHJpYnV0ZXMgb2YgSFRNTCB0YWdzLiAgVG8gbWluZSB0aGUgVVJMIG9mIGEgaHlwZXJ0ZXh0IGxpbmsgYDxhIGhyZWY9IlVSTCI+PC9hPmAsIHdpdGhpbiBhIGxpc3QgaXRlbSwgeW91IG5lZWQgdG8gcGFyc2UgdGhlIEhSRUYgYXJndW1lbnQgb2YgYW4gYW5jaG9yIHRhZy4gIElmIHlvdSdyZSBuZXcgdG8gd2ViIHNjcmFwaW5nLCB5b3UncmUgZ29pbmcgdG8gbmVlZCB0byBsZWFybiBzb21ldGhpbmcgYWJvdXQgSFRNTCB0YWdzLCBzdWNoIGFzIHRoZSBbYW5jaG9yIHRhZ10oaHR0cHM6Ly93d3cudzNzY2hvb2xzLmNvbS9UQUdTL3RhZ19hLmFzcCkuDQoNCioqRm9yIEV4YW1wbGUqKg0KDQppbiBhbiBIVE1MIGRvY3VtZW50IHRoYXQgaGFzIHRhZ2dpbmcgc3VjaCBhcyB0aGlzOg0KDQpgYGBodG1sDQo8YSBocmVmPSJodHRwczovL3NlYXJjaC5jb20iPkV4YW1wbGUgTGluazwvYT4NCmBgYCANCg0KSSB3YW50IHRvIGdhdGhlciB0aGUgdmFsdWUgb2YgdGhlIGBocmVmYCBhdHRyaWJ1dGUgd2l0aGluIHRoZSBhbmNob3IgdGFnOiAgKipodHRwczovL3NlYXJjaC5jb20qKg0KDQoqKkNPREUqKg0KDQpVc2luZyB0aGUgYGh0bWxfbm9kZXMoKWAgYW5kIGBodG1sX2F0dHIoKWAgZnVuY3Rpb25zLCBJIGNhbiByZXRyaWV2ZSBhbGwgdGhlIGF0dHJpYnV0ZSB2YWx1ZXMgd2l0aGluIGA8bGk+PGE+PC9hPjwvbGk+YCB0YWdzLg0KDQpgYGB7cn0NCnVybCA8LSByZXN1bHRzICU+JSANCiAgaHRtbF9ub2RlcygibGkgYSIpICU+JSANCiAgaHRtbF9hdHRyKCJocmVmIikNCg0KdXJsDQpgYGANCg0KTm90ZSB0aGF0IHRoZSBhYm92ZSBsaW5rcywgb3IgX2hyZWZzXywgYXJlIHJlbGF0aXZlIFVSTCBwYXRocy4gIEkgc3RpbGwgbmVlZCB0aGUgZG9tYWluIG5hbWUgZm9yIHRoZSB3ZWIgc2VydmVyIGBodHRwOi8vd3d3LnZvbmRlbC5odW1hbml0aWVzLnV2YS5ubGAuDQoNCiMjIFN5c3RlbWF0aXplDQoNCkFib3ZlIEkgY3JlYXRlZCB0d28gdmVjdG9ycywgb25lIHZlY3RvciwgYG5hbWVzYCwgaXMgdGhlIGBodG1sX3RleHRgIHRoYXQgSSBwYXJzZWQgZnJvbSB0aGUgYDxsaT5gIHRhZ3Mgd2l0aGluIHRoZSBgPGJvZHk+YCBvZiB0aGUgSFRNTCBkb2N1bWVudC4gIFRoZSBvdGhlciB2ZWN0b3IsIGB1cmxgLCBpcyBhIHZlY3RvciBvZiB0aGUgdmFsdWVzIG9mIHRoZSBgaHJlZmAgYXR0cmlidXRlIG9mIHRoZSBhbmNob3IgYDxhPmAgdGFncy4gDQoNClBsYWNpbmcgYm90aCB2ZWN0b3JzIGludG8gYSB0aWJibGUgbWFrZXMgbWFuaXB1bGF0aW9uIGVhc2llciB3aGVuIHVzaW5nIHRpZHl2ZXJzZSB0ZWNobmlxdWVzLg0KDQoqKkdvYWwqKg0KDQpJIHdhbnQgdG8gZGV2ZWxvcCBhIHN5c3RlbWF0aWMgd29ya2Zsb3cgdGhhdCBidWlsZHMgYSB0aWJibGUgY29uc2lzdGluZyBvZiBlYWNoIG9mIHRoZSA1MCBuYW1lcyBsaXN0ZWQgaW4gdGhlIHN1bW1hcnkgYHJlc3VsdHNgIG9iamVjdCByZXRyaWV2ZWQgYnkgYHJlYWRfaHRtbCgpYCBwZXJmb3JtZWQgb24gW3RoaXMgcGFnZV0oaHR0cDovL3d3dy52b25kZWwuaHVtYW5pdGllcy51dmEubmwvZWNhcnRpY28vcGVyc29ucy9pbmRleC5waHA/c3VidGFzaz1icm93c2UpLiAgT2YgY291cnNlLCBtaW5pbmcgYW5kIHBhcnNpbmcgdGhlIGRhdGEgaXMganVzdCB0aGUgYmVnaW5uaW5nLiAgRGF0YSBjbGVhbmluZyBpcyBhIHZpdGFsIGFuZCBjb25zdGFudCBhc3BlY3Qgb2Ygd2ViIHNjcmFwaW5nLg0KDQojIyMgQnVpbGQgVGliYmxlDQoNClVzaW5nIHZlY3RvcnMgZnJvbSBwYXJzaW5nIGZ1bmN0aW9ucyBhYm92ZS4uLi4NCg0KYGBge3J9DQpyZXN1bHRzX2RmIDwtIHRpYmJsZShuYW1lcywgdXJsKQ0KDQpyZXN1bHRzX2RmDQpgYGANCg0KRnJvbSBhYm92ZSB3ZSBoYXZlIGxpbmtzIGluIGEgYHVybGAgdmVjdG9yLCBhbmQgdGFyZ2V0IG5hbWVzIGluIGEgYG5hbWVzYCB2ZWN0b3IsIGZvciBmaWZ0eSBuYW1lcyBmcm9tIHRoZSB0YXJnZXQgd2Vic2l0ZSB3ZSB3YW50IHRvIGNyYXdsLiAgT2YgY291cnNlIHlvdSBhbHNvIHdhbnQgdG8gcGFyc2UgZGF0YSBmb3IgIGVhY2ggcGVyc29uIGluIHRoZSBkYXRhYmFzZS4gIFRvIGRvIHRoaXMgd2UgbmVlZCB0byByZWFkIChpLmUuIGByZWFkX2h0bWwoKWApIHRoZSBIVE1MIGZvciBlYWNoICByZWxldmFudCBgdXJsYCBpbiB0aGUgcmVzdWx0c19kZiB0aWJibGUuICBCZWxvdyBpcyBhbiBleGFtcGxlIG9mIGhvdyB0byBzeXN0ZW1hdGl6ZSB0aGUgd29ya2Zsb3cuIFRvIGRvIHRoYXQsIHdlJ2xsIG1ha2UgYSByZXN1bHRzIHRpYmJsZSwgYHJlc3VsdHNfZGZgLiAgQnV0IGZpcnN0LCBtb3JlIGRhdGEgY2xlYW5pbmcuLi4NCg0KQ3JlYXRlIHNvbWUgbmV3IHZhcmlhYmxlcyB3aXRoIGBtdXRhdGVgLiAgQnVpbGQgYSAqKmZ1bGwqKiBVUkwgZnJvbSB0aGUgX3JlbGF0aXZlIFVSTCBwYXRoXyAoaS5lLiB0aGUgYHVybGAgdmVjdG9yKSBhbmQgdGhlIGRvbWFpbiBvciBfYmFzZSBVUkxfIG9mIHRoZSB0YXJnZXQgc2l0ZS4gIFNpbmNlIHdlIHNjcmFwZWQgdGhlIHJlbGF0aXZlIFVSTCAqKnBhdGgqKiwgd2UgaGF2ZSB0byBjb25zdHJ1Y3QgYSAqKmZ1bGwqKiBVUkwuDQoNCmBgYHtyfQ0KdXJsc190b19jcmF3bF9kZiA8LSByZXN1bHRzX2RmICU+JSANCiAgbXV0YXRlKGZ1bGxfdXJsID0gZ2x1ZTo6Z2x1ZSgiaHR0cDovL3d3dy52b25kZWwuaHVtYW5pdGllcy51dmEubmx7dXJsfSIpKSAlPiUgDQogIG11dGF0ZShmdWxsX3VybCA9IHN0cl9yZXBsYWNlX2FsbChmdWxsX3VybCwgIlxcLlxcLiIsICIiKSkgJT4lIA0KICBzZWxlY3QoZnVsbF91cmwpDQoNCnVybHNfdG9fY3Jhd2xfZGYgIA0KYGBgDQoNCkFzIHlvdSBjYW4gc2VlLCBhYm92ZSwgaXQncyByZWFsbHkgaGVscGZ1bCB0byBrbm93IGFib3V0IFRpZHl2ZXJzZSB0ZXh0IG1hbmlwdWxhdGlvbiwgc3BlY2lmaWNhbGx5IGBtdXRhdGVgLCAgW2BnbHVlYF0oaHR0cHM6Ly9nbHVlLnRpZHl2ZXJzZS5vcmcvKSwgYW5kIFtwYXR0ZXJuIG1hdGNoaW5nIGFuZCByZWdleF0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9zdHJpbmdzLmh0bWwpIHVzaW5nIHRoZSBbc3RyaW5nciBwYWNrYWdlXShodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZy8pLg0KDQojIyMgT3BlcmF0aW9uYWxpemUgdGhlIHdvcmtmbG93DQoNClRvIG9wZXJhdGlvbmFsaXplIHRoaXMgcGFydCBvZiB0aGUgd29ya2Zsb3csIHlvdSB3YW50IHRvIGl0ZXJhdGUgb3ZlciB0aGUgdmVjdG9yIGBmdWxsX3VybGAgZm91bmQgaW4gdGhlIGB1cmxzX3RvX2NyYXdsX2RmYCB0aWJibGUuIFRoZW4gYHJlYWRfaHRtbGAgZm9yIGVhY2ggbmFtZSB0aGF0IGludGVyZXN0IHlvdS4gIFJlbWVtYmVyIHRoYXQgb25seSA1MCBvZiB0aGUgNTQgcm93cyBpbiB0aGUgYHJlc3V0bHNfZGZgIHRpYmJsZSBhcmUgdGFyZ2V0IG5hbWVzIHRvIGNyYXdsLiBTbywgcmVhbGx5LCB5b3Ugc3RpbGwgaGF2ZSBzb21lIGRhdGEgd3JhbmdsaW5nIHRvIGRvLiAgSG93IGNhbiB5b3UgZWxpbWluYXRlIHRoZSBmb3VyIHJvd3MgaW4gdGhlIGByZXN1bHRzX2RmYCB0aWJibGUgdGhhdCBhcmUgbm90IHRhcmdldHMgKGkuZS4gbmFtZXMpPyAgU29tZXdoZXJlLCBiZWxvdywgSSdsbCBhbHNvIHNob3cgeW91IGhvdyB0byBleGNsdWRlIHRoZSBmb3VyIHJvd3Mgb2YgdW5uZWNlc3NhcnkvdW5oZWxwZnVsIGluZm9ybWF0aW9uLg0KDQojIyBOYXZpZ2F0aW9uIGFuZCBjcmF3bGluZw0KDQpNZWFud2hpbGUsIHdlIHN0aWxsIGhhdmUgdGhlICoqZ29hbCB0byBzeXN0ZW1hdGljYWxseSBjcmF3bCB0aGUgc2l0ZSdzIG5hdmlnYXRpb24gbGlua3MqKiBmb3IgZWFjaCBvZiB0aGUgMjArIHN1bW1hcnkgcmVzdWx0cyBwYWdlcywgZWFjaCBvZiB3aGljaCBjb25zaXN0cyBvZiBmaWZ0eSBuYW1lcy4gIFJlbWVtYmVyLCBlYWNoIHN1bW1hcnkgcmVzdWx0cyBwYWdlIGFsc28gaGFzIGxpbmtzIHRvIHdlYiBzaXRlICoqbmF2aWdhdGlvbioqLiAgTmF2aWdhdGlvbiBsaW5rcyBhcmUgdGhlICoqa2V5IHRvIGNyYXdsaW5nKiogdGhyb3VnaCB0aGUgb3RoZXIgc3VtbWFyeSByZXN1bHRzIHBhZ2VzLiAgWW91IHdpbGwgbmVlZCB0byB3cml0ZSBjb2RlIHRoYXQgY3Jhd2xzIHRocm91Z2ggdGhlIG5hdmlnYXRpb24gbGlua3MsIHRoZW4gYHJlYWRfaHRtbCgpYCBmb3IgZWFjaCBvZiB0aGUgZmlmdHkgbmFtZS91cmxzIGluIHRoYXQgcGFydGljdWxhciBzdW1tYXJ5IHJlc3VsdHMgcGFnZS4gIA0KDQpUaGVyZWZvcmUsIG9uZSBjcmF3bGluZyAqKmdvYWwqKiBpcyB0byBidWlsZCB1cCBhIHRpYmJsZSB0aGF0IGNvbnRhaW5zIFVSTFMgZm9yIGVhY2ggc3VtbWFyeSByZXN1bHRzIHBhZ2UuICBTbyBmYXIgd2Ugb25seSBoYXZlIGxpbmtzIHRvIGZpZnR5IG5hbWVzIGZyb20gdGhlIGZpcnN0IHN1bW1hcnkgcmVzdWx0cyBwYWdlLiAgV2UgZG9uJ3QgaGF2ZSB0aGUgVVJMcyBmb3IgZWFjaCBvZiB0aGUgMjArIG5hdmlnYXRpb24gcGFnZXMuIEhvdyBkbyB3ZSBnZXQgdGhvc2U/IEZpcnN0IGZpbmQgdGhlIHBhdHRlcm4gZm9yIHRoZSBsaW5rcyB0byB0aGUgbmF2aWdhdGlvbiBwYWdlcy4gIExldCdzIGdldCB0aGUgbmF2aWdhdGlvbiBsaW5rIHRvIHRoZSBbc2Vjb25kIHN1bW1hcnkgcmVzdWx0cyBwYWdlXShodHRwOi8vd3d3LnZvbmRlbC5odW1hbml0aWVzLnV2YS5ubC9lY2FydGljby9wZXJzb25zL2luZGV4LnBocD9zdWJ0YXNrPWJyb3dzZSZmaWVsZD1zdXJuYW1lJnN0cnRjaGFyPUEmcGFnZT0yKS4gIA0KDQpgYGBodG1sDQpodHRwOi8vd3d3LnZvbmRlbC5odW1hbml0aWVzLnV2YS5ubC9lY2FydGljby9wZXJzb25zL2luZGV4LnBocD9zdWJ0YXNrPWJyb3dzZSZmaWVsZD1zdXJuYW1lJnN0cnRjaGFyPUEmcGFnZT0yDQpgYGANCg0KQWZ0ZXIgc29tZSBpbnZlc3RpZ2F0aW9uIG9mIHRoZSB0aGUgSFRNTCBzb3VyY2UsIEkgc2VlIHRoZSBvbmx5IGRpZmZlcmVuY2UgYmV3dGVlbiBuYXZpZ2F0aW9uIFVSTFMgaXMgdGhlIHZhbHVlIG9mIHRoZSBVUkwgYXJndW1lbnQgYHBhZ2U9YC4gIEV2ZW50dWFsbHkgd2UgbmVlZCB0byBjb25zdHJ1Y3QgYSB0aWJibGUgd2l0aCBhbGwgdGhlIFVSTHMgZm9yIGVhY2ggb2YgdGhlIG5hdmlnYXRpb24gcGFnZXMuICBCdXQgZmlyc3QsIGxldCdzIGxlYXJuIGEgYml0IG1vcmUgYWJvdXQgSFRNTCBwYXJzaW5nLg0KDQojIyMgRGVjb25zdHJ1Y3RpbmcgYSBzaW5nbGUgdGFyZ2V0IHBhZ2UgYW5kIHNpdGUgZGVzaWduDQoNClNvIGZhciB3ZSd2ZSB1c2VkIGBydmVzdGAgZnVuY3Rpb25zIGFuZCBsZWFybmVkIGEgbGl0dGxlIEhUTUwuICBZb3UgKiphbHNvIG5lZWQgdG8ga25vdyBhYm91dCoqIFsqKkNTUyoqIChDYXNjYWRpbmcgU3R5bGUgU2hlZXRzKV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ1NTKS4gIA0KDQpZb3UgY2FuIGZvbGxvdyB0aGUgbGluayBhYm92ZSBmb3IgYSBkZWZpbml0aW9uIG9mIENTUy4gIEFzIGEgcXVpY2tzdGFydCBhcHByYW9jaCwgSSByZWNvbW1lbmQgcGxheWluZyB0aGlzIFtDU1MgZ2FtZV0oaHR0cHM6Ly9mbHVrZW91dC5naXRodWIuaW8vKSBhcyBhIGZ1biBhbmQgcXVpY2sgd2F5IHRvIGxlYXJuIGp1c3QgZW5vdWdoIENTUy4gIEkgY29tcGxldGVkIHRoZSBmaXJzdCAxNSBsZXZlbHMgb2YgdGhlIGdhbWU7IHRoYXQgd2FzIGVub3VnaCB0byBnZXQgYSBnb29kIGZvdW5kYXRpb24gd2l0aCBDU1MuICBEZXBlbmRpbmcgb24gdGhlIGNvbXBsZXhpdHkgb2YgdGhlIEhUTUwgYW5kIENTUywgeW91IG1heSBuZWVkIHRvIGtub3cgYSBsaXR0bGUgbW9yZSBvciBhIGxpdHRsZSBsZXNzLiAgQnV0IHlvdSBuZWVkIHRvIGtub3cgc29tZXRoaW5nIGFib3V0IEhUTUwgYW5kIENTUy4NCg0KKipDU1MqKiB3aWxsIGhlbHAgdXMgc3Vic2V0IHRoZSBIVE1MIGZvciBhIHNpbmdsZSB0YXJnZXQgcGFnZS4gIEkgZGlkbid0IG1lbnRpb24gaXQgYmVmb3JlIGJ1dCwgeW91IG9mdGVuIG5lZWQgdG8gW3ZpZXcgdGhlIHNvdXJjZSBvZiB0aGUgSFRNTF0oaHR0cHM6Ly93d3cubGlmZXdpcmUuY29tL3ZpZXctaHRtbC1zb3VyY2UtaW4tY2hyb21lLTM0NjY3MjUpOyB1c2UgdGhlIFtjaHJvbWUgYnJvd3NlciB0byBpbnNwZWN0IGVsZW1lbnRzXShodHRwczovL3d3dy53aWtpaG93LmNvbS9JbnNwZWN0LUVsZW1lbnQtb24tQ2hyb21lKSBvZiBwYWdlcyBpbiBhIHdlYiBicm93c2VyOyBhbmQgdXNlIHRoZSBjaHJvbWUgYnJvd3NlciBleHRlbnNpb24sIFtzZWxlY3RvciBnYWRnZXRdKGh0dHBzOi8vcnZlc3QudGlkeXZlcnNlLm9yZy9hcnRpY2xlcy9zZWxlY3RvcmdhZGdldC5odG1sKSwgdG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhlIEhUTUwgYW5kIENTUyB0YWdnaW5nIGFuZCBzdHJ1Y3R1cmUuICANCg0KVGhpcyBpcyBrZXkuICBXaGVuIHdlYiBzY3JhcGluZywgeW91IGFyZSBlZmZlY3RpdmVseSByZXZlcnNlIGVuZ2luZWVyaW5nIHRoZSB3ZWIgc2l0ZS4gIFRvIHJldmVyc2UgZW5naW5lZXIgaXQsIHlvdSBtdXN0IGhhdmUgYSBzb2xpZCB1bmRlcnN0YW5kaW5nIG9mIHRoZSB0YXJnZXQgc2l0ZSdzIHN0cnVjdHVyZSwgdGhlIHNpdGUgbmF2aWdhdGlvbiwgSFRNTCwgYW5kIENTUy4gIEtub3dsZWRnZSBvZiB0aGUgc2l0ZSBzdHJ1Y3R1cmUgaXMgaW5kZXBlbmRlbnQgb2YgUiwgYHJ2ZXN0YCwgX2xpc3RzXywgYW5kIGBwdXJycmAuDQoNCkFueXdheSwgYW4gZXhhbXBsZSBvZiBhIENTUyBlbGVtZW50IGluIHRoZSByZXN1bHRzIHBhZ2UgaXMgJ2NsYXNzJyBhdHRyaWJ1dGUgb2YgdGhlIGA8ZGl2PmAgdGFnLiAgSW4gdGhlIGNhc2UgYmVsb3cgd2UgYWxzbyBoYXZlIGEgY2xhc3MgdmFsdWUgb2YgInN1Ym5hdiIuICBWaWV3aW5nIHRoZSBzb3VyY2UgSFRNTCBvZiBvbmUgc3VtbWFyeSByZXN1bHRzIHBhZ2Ugd2lsbCBzaG93IHRoZSBgPGRpdj5gIHRhZ3Mgd2l0aCB0aGUgQ1NTIF9jbGFzc18gZWxlbWVudC4NCg0KKipGb3IgRXhhbXBsZSoqICAgDQoNClVzZSBpcyBgaHRtbF9ub2RlcygpYCB3aXRoIGBodG1sX3RleHQoKWAsIGFuZCBgaHRtbF9hdHRyKClgIHRvIHBhcnNlIHRoZSBhbmNob3IgdGFnIG5vZGVzLCBgPGE+YCwgZm91bmQgd2l0aGluIHRoZSBgPGRpdj5gIHRhZ3Mgd2hpY2ggY29udGFpbiB0aGUgYGNsYXNzPSJzdWJuYXYiYCBhdHRyaWJ1dGUuDQoNCmBgYGh0bWwNCjxkaXYgY2xhc3MgPSAic3VibmF2Ij4NCiAgICAuLi4NCiAgICA8YSBocmVmPSIvZWNhcnRpY28vcGVyc29ucy9pbmRleC5waHA/c3VidGFzaz1icm93c2UmYW1wO2ZpZWxkPXN1cm5hbWUmYW1wO3N0cnRjaGFyPUEmYW1wO3BhZ2U9MyI+WzEwMS0xNTBdPC9hPg0KICAgIC4uLg0KPC9kaXY+DQpgYGANCg0KKipDT0RFKiogIA0KDQpQYXJzZSB0aGUgKip0ZXh0Kiogb2YgdGhlIG5hdmlnYXRpb24gYmFyLg0KDQpgYGB7cn0NCnJlc3VsdHMgJT4lICNodG1sX25vZGVzKCJkaXYuc3VibmF2IikNCiAgaHRtbF9ub2RlcygiZGl2LnN1Ym5hdiBhIikgJT4lIA0KICBodG1sX3RleHQoKQ0KDQpgYGANCg0KUGFyc2UgdGhlIEhUTUwgX2hyZWZfICoqYXR0cmlidXRlKiogdG8gZ2V0IHRoZSBVUkwuDQoNCmBgYHtyIGxvb3B9DQpuYXZpZ2F0aW9uIDwtIHJlc3VsdHMgJT4lIA0KICBodG1sX25vZGVzKCJkaXYuc3VibmF2IGEiKSAlPiUgDQogIGh0bWxfYXR0cigiaHJlZiIpDQoNCm5hdmlnYXRpb24NCmBgYA0KDQojIyMgQ3Jhd2wgc3VtbWFyeSByZXN1bHRzIHBhZ2VzDQoNCldlIHdhbnQgdG8gZ2FpbiBhIGNsZWFyIHNlbnNlIG9mIHRoZSBwcmVkaWN0YWJsZSBuYXZpZ2F0aW9uIHN0cnVjdHVyZSBvZiBvdXIgdGFyZ2V0IHNpdGUsIGFuZCB0aGUgbmF2aWdhdGlvbiBVUkxzLCBzbyB0aGF0IHdlIGNhbiAqKmNyYXdsKiogdGhyb3VnaCBlYWNoIHBhZ2Ugb2YgdGhlIHRhcmdldCBzaXRlLiAgQWdhaW4sIHRoZSB0YXNrIGJlZm9yZSB1cyBpcyB0byBkbyB0aGlzIGNyYXdsaW5nIHN5c3RlbWF0aWNhbGx5LiAgTGV0J3MgcHV0IHRoZSBgbmF2aWdhdGlvbmAgb2JqZWN0IGludG8gYSB0aWJibGUgZGF0YS1mcmFtZSBjYWxsZWQgYG5hdl9kZmAuDQoNCmBgYHtyfQ0KbmF2X2RmIDwtIHRpYmJsZShuYXZpZ2F0aW9uKQ0KbmF2X2RmDQpgYGANCg0KSW4gdGhlIGFib3ZlIHRpYmJsZSwgSSBzZWUgSSBuZWVkIHRvICoqY2xlYW4qKiB0aGUgYG5hdl9kZiRuYXZpZ2F0aW9uYCB2ZWN0b3Igc28gdGhhdCBvbmx5IHRoZSB1bmlxdWUgVVJMcyBmb3IgZWFjaCA1MC1yZXN1bHQgc3VtbWFyeSBwYWdlIHJlbWFpbi4gIE9uZSBwcm9ibGVtIGlzIHRoZXJlIGFyZSBvbmx5IDE0IHJvd3MgYW5kIGEgbG90IG9mIHJlZHVuZGFuY3kgYW5kIG1hbnkgbWlzc2luZyBzdW1tYXJ5IHBhZ2UgVVJMUy4gIE9uZSByZWFzb24gSSBrbm93IHRoaXMgaXMgdGhhdCBJIGhhdmUgYSBsaW5rIGZvciBwYWdlIDIsIDMsIDQsIDUsIGFuZCAyMjsgYnV0IG5vdGhpbmcsIG5vIFVSTHMgZm9yIHBhZ2VzIGJldHdlZW4gcGFnZXMgNiBhbmQgMjEuICBUaGlzIG1pcnJvcnMgd2hhdCB3ZSBzZWUgd2hlbiB3ZSBbdmlldyB0aGUgYWN0dWFsIHRhcmdldCB3ZWIgcGFnZSBpbiBhIHdlYiBicm93c2VyXShodHRwOi8vd3d3LnZvbmRlbC5odW1hbml0aWVzLnV2YS5ubC9lY2FydGljby9wZXJzb25zL2luZGV4LnBocD9zdWJ0YXNrPWJyb3dzZSkuICBUaGF0IGlzLCBJIGhhdmUgbGlua3MgZm9yICoqcmVzdWx0cyoqIFsxLTUwXSwgWzUxLTEwMF0sIFsxMDAtMTUwXSwgYnV0IG5vdGhpbmcgZm9yIFsyNTEtMjYwXSAoaS5lLiBwYWdlIDYpLCBldGMuICBUaGUgdGFyZ2V0IHBhZ2UgaXMgY29tcG9zZWQgb2YgSFRNTCBhbmQgQ1NTIGFuZCBjb21wcmlzZXMgd2hhdCB3ZSBoYXZlIGluIG91ciBpbXBvcnRlZCBgcmVzdWx0c2Agb2JqZWN0LCBhYm92ZS4gIFRvIGNvbmZpcm0gbXkgaW52ZXN0aWdhdGlvbiwgSSdsbCB1c2UgYSB3ZWIgYnJvd3NlciB0byAqKnZpZXcqKiB0aGUgSFRNTCAqKnNvdXJjZSoqIG9mIG9uZSBvZiB0aGUgc3VtbWFyeSBwYWdlcy4gIFRoZSB1bnBhcnNlZCBIVE1MIGlzIHdoYXQgeW91IGhhdmUgaW4gYHJlc3VsdHNgIG9iamVjdC4gIA0KDQpXaGF0IEkgc2VlIGluIHRoZSB2aWV3IHNvdXJjZSBvciB0aGUgYG5hdl9kZiRuYXZpZ2F0aW9uYCB2ZWN0b3IgaXMgdGhhdCBlYWNoIG5hdmlnYXRpb24gVVJMIGlzIHRoZSBzYW1lLCAqKmV4Y2VwdCoqIGZvciB0aGUgZGlmZmVyZW5jZSBpbiB0aGUgYHBhZ2U9YCBlbGVtZW50IGZvdW5kIGF0IHRoZSBlbmQgb2YgdGhlIFVSTCBhcmd1bWVudC4gIA0KDQojIyMgTWluaSByZXZpZXcgc3VtbWFyeQ0KDQpXZSBjYW4gc2VlIHRoYXQgdGhlIG5hdmlnYXRpb24gVVJMIHBhdHRlcm4gaXMgcHJlZGljdGFibGUuICBXZSBjYW4gY29uc3RydWN0IGEgZnVsbCBVUkwgZm9yIGVhY2ggc3VtbWFyeSByZXN1bHRzIHRhcmdldCBwYWdlLiBXZSBjYW4gbWFrZSBhIHRpYmJsZSB3aXRoIGFsbCB0aGVzZSBsaW5rcywgb25lIHJvdyBmb3IgZWFjaCBVUkwgdG8gZWFjaCBuYXZpZ2F0aW9uIHNlY3Rpb24gb2YgZWFjaCBzdW1tYXJ5IHJlc3VsdHMgcGFnZSBvZiB0aGUgdGFyZ2V0IHNpdGUuIE9uY2Ugd2UgaGF2ZSB0aGlzLCB3ZSBjYW4gd3JpdGUgYSBzY3JpcHQgdG8gc3lzdGVtYXRpY2FsbHkgYnJvd3NlLCBvciBjcmF3bCwgdGhyb3VnaCB0aGUgdGFyZ2V0IHNpdGUganVzdCBhcyBpZiB3ZSBtYW51YWxseSBtb3VzZS1jbGlja2VkIGVhY2ggbGluayBpbiBhIHdlYiBicm93c2VyLiAgV2Ugd2lsbCB1c2UgdGlkeXZlcnNlIHBhY2thZ2VzIHRvIGNyYXdsIHRocm91Z2ggdGhlIHNpdGUgYW5kIGBydmVzdDo6aHRtbF9yZWFkKClgIHRvIGltcG9ydCB0aGUgSFRNTC4gIEFmdGVyIHdlIGNyYXdsIGVhY2ggcGFnZSwgd2UnbGwgcGFyc2UgdGhlIHRhcmdldCBIVE1MIGRhdGEuDQoNCiMjIyBEZXZlbG9wIGEgcGxhbiANCg0KKipHb2FsKiogbWFrZSBhIHRpYmJsZSB3aXRoIGFsbCB0aGUgbmF2aWdhdGlvbiBsaW5rcw0KDQpMZXQncyBzdGFydCBieSBpbXBvcnRpbmcgdGhlIEhUTUwgb2YgYSBzaW5nbGUgc3VtbWFyeSByZXN1bHRzIHBhZ2UuICBUaGVuIEknbGwgbWFrZSBhIHRpYmJsZSBvZiB0aGUgbmF2aWdhdGlvbiBsaW5rcyBmb3VuZCB3aXRoaW4gdGhlIGdhdGhlcmVkIGFuZCByZWxldmFudCBgPGRpdj5gIEhUTUwgdGFncywgaS5lLiB0aGUgZGl2IHRhZ3Mgd2l0aCB0aGUgX3N1Ym5hdl8gQ1NTIGNsYXNzLiAgV2UgZGlkIHRoYXQgd2l0aCB0aGUgYG5hdl9kZmAgdGliYmxlLg0KDQpBZnRlciB3ZSBnYXRoZXIgdGhvc2UgbmF2aWdhdGlvbiBsaW5rcyBpbiBgbmF2X2RmJG5hdmlnYXRpb25gLCB3ZSB3aWxsIHdyYW5nbGUgYW5kIGV4cGFuZCB0aGUgdmVjdG9yIHRvIGluY2x1ZGUgb25seSB0aGUgcmVsZXZhbnQgbmF2aWdhdGlvbiBVUkxzLiAgQnV0LCB3aHkgZGlkIHdlIGhhdmUgZHVwbGljYXRlIG5hdmlnYXRpb24gVVJMUyBpbiB0aGF0IHZlY3Rvcj8gIExvb2sgYXQgdGhlIFtzdW1tYXJ5IHBhZ2VdKGh0dHA6Ly93d3cudm9uZGVsLmh1bWFuaXRpZXMudXZhLm5sL2VjYXJ0aWNvL3BlcnNvbnMvaW5kZXgucGhwP3N1YnRhc2s9YnJvd3NlKSBpbiBhIHdlYiBicm93c2VyLiAgU2VlIGFueSBkdXBsaWNhdGlvbj8gIElmIG5vdCwgbG9vayBjbG9zZXIuDQoNCg0KQmVsb3csIHVzZSBgZHBseXI6OmRpc3RpbmN0KClgIGFuZCBgc3RyaW5ncjo6c3RyaW5nX2V4dHJhY3QoKWAgYW1vbmcgb3RoZXIgdGlkeXZlcnNlIGZ1bmN0aW9ucyB0byB3cmFuZ2xlIGEgdGliYmxlIG9mIHJlbGV2YW50IGxpbmtzLiAgDQoNCmBgYHtyfQ0KbmF2X2RmIDwtIG5hdl9kZiAlPiUNCiAgZmlsdGVyKHN0cl9kZXRlY3QobmF2aWdhdGlvbiwgIiZwYWdlPSIpKSAlPiUNCiAgZGlzdGluY3QobmF2aWdhdGlvbikgJT4lDQogIG11dGF0ZShwYWdlX25vID0gc3RyX2V4dHJhY3QobmF2aWdhdGlvbiwgIlxcZCskIikpICU+JQ0KICBtdXRhdGUocGFnZV9ubyA9IGFzLm51bWVyaWMocGFnZV9ubykpDQoNCm5hdl9kZg0KYGBgDQoNClRoZXJlJ3Mgc29tZSBfZmFuY3lfIHJlZ2V4IHBhdHRlcm4gbWF0Y2hpbmcgdGFraW5nIHBsYWNlIGluIHRoZSBkYXRhIHdyYW5nbGluZyBjb2RlIGNodW5rLCBhYm92ZS4gIFJlbWVtYmVyIGhvdyBJIHNhaWQgeW91IG5lZWQgdG8gbGVhcm4gYWJvdXQgcGF0dGVybiBtYXRjaGluZyBhbmQgdGhlIHN0cmluZ3IgbGlicmFyeT8gIFl1cC4NCg0KQW55d2F5Li4uDQoNCk5vdyBhbGwgd2UgbmVlZCB0byBkbyBpcyBleHBhbmQgdGhlIHNlcXVlbmNlIG9mIHBhZ2VzLiAgKEhpbnQ6IGB0aWR5cjo6ZXhwYW5kKClgICkNCg0KVGhlIG1heGltdW0gbnVtYmVyIG9mIHN1bW1hcnkgcGFnZXMgaW4gdGhlIGBuYXZpZ2F0aW9uJHBhZ2Vfbm9gIHZhcmlhYmxlIGlzIDIyLiAgVGhpcyBzaG91bGQgbWVhbiB0aGUgbWF4aW11bSBudW1iZXIgb2YgVVJMcyB0byBzdW1tYXJ5IHJlc3VsdHMgcGFnZXMgd2lsbCBiZSByb3VnaGx5IDIyLiBSZWdhcmRsZXNzIG9mIHRoZSB0b3RhbCBudW1iZXIgb2YgdGFyZ2V0IG5hbWVzL3BhZ2VzLCBvdXIgdGFzayBpcyB0byBidWlsZCBhIHRpYmJsZSB3aXRoIGEgVVJMIGZvciBlYWNoIHN1bW1hcnkgcmVzdWx0cyBwYWdlLCBpLmUuIHBhZ2VzIDEgdGhvcm91Z2ggMjIuICBJRiB3ZSBoYXZlIGEgbGluayB0byBlYWNoIHN1bWFtcnkgcmVzdWx0cyBwYWdlLCB0aGVuIGNhbiB3ZSBnZXQgYSBsaW5rIGZvciBlYWNoIG9mIHRoZSBmaWZ0eSBwZW9wbGUgbGlzdGVkIG9uIGVhY2ggb2YgdGhlIHN1bW1hcnkgcmVzdWx0IHBhZ2VzLiAgDQoNCkhvbmVzdGx5LCBidWlsZGluZyB0aGlzIGxpc3Qgb2YgbmF2aWdhdGlvbiBVUkxzIHRha2VzIHNvbWUgZWZmb3J0IGluIFIsIGVzcGVjaWFsbHkgaWYgeW91J3JlIG5ldyB0byBSLiAgU28sIG1heWJlLCB0aGVyZSdzIGFuIGVhc2llciB3YXkuICBJdCBtaWdodCBiZSBlYXNpZXIgdG8gYnVpbGQgdGhlIHJhbmdlIG9mIHN1bW1hcnkgcGFnZSBVUkxzIGluIEV4Y2VsLCB0aGVuIGltcG9ydCB0aGUgZXhjZWwgZmlsZSAob3IgQ1NWIGZpbGUpIG9mIFVSTHMgaW50byBSIGZvciBjcmF3bGluZyB2aWEgYHJ2ZXN0YC4gIEJ1dCBJIHdhbnQgYSByZXByb2R1Y2libGUsIGNvZGUtYmFzZWQgYXBwcm9hY2guICANCg0KU2VlIGV4YW1wbGUgY29kZSwgYmVsb3csIGZvciBhIHJlcHJvZHVjaWJsZSBleGFtcGxlLiAgV2l0aCB0aGUgbmV4dCBjb2RlIGNodW5rLCB5b3UgY2FuIHVzZSBUaWR5dmVyc2UgdGVjaG5pcXVlcyBhbmQgYnVpbGQgYSB0aWJibGUgb2YgdXJscyB0byBpdGVyYXRlIG92ZXIuIEluIHRoaXMgY2FzZSwgdGhlIGltcG9ydGFudCByZXByb2R1Y2libGUgc3RlcCB1c2UgYHN0cmluZ3I6OnN0cl9leHRyYWN0KClgIHRvIGZpbmQgYW5kIG1hdGNoIHRoZSBVUkwgcGF0dGVybi4gIA0KDQpgYGB7cn0NCm5hdl9kZiA8LSBuYXZfZGYgJT4lIA0KICBtdXRhdGUobmF2aWdhdGlvbiA9IHN0cl9leHRyYWN0KG5hdmlnYXRpb24sICIuKig/PVxcPVxcZCskKSIpKSAlPiUgDQogIG11dGF0ZShwYWdlX25vID0gYXMuaW50ZWdlcihzdHJfcmVwbGFjZShwYWdlX25vLCAiXjIkIiwgIjEiKSkpICU+JSANCiAgZXhwYW5kKG5hdmlnYXRpb24sIHBhZ2Vfbm8gPSBmdWxsX3NlcShwYWdlX25vLCAxKSkgJT4lIA0KICB0cmFuc211dGUodXJsID0gZ2x1ZTo6Z2x1ZSgiaHR0cDovL3d3dy52b25kZWwuaHVtYW5pdGllcy51dmEubmx7bmF2aWdhdGlvbn09e3BhZ2Vfbm99IikpDQoNCiMgdGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiAnLiooPz1cXD1cXGQrJCknIGNhbiBiZSByZWFkIGFzOiANCiMgY2FwdHVyZS9maW5kIGRhdGEgZm91bmQgQkVGT1JFICcoPz08PHBhdHRlcm4+PiknIHRoZSBwYXR0ZXJuLiAgDQojIFRoZSBwYXR0ZXJuIGlzID08ZGlnaXQ+PGRpZ2l0PiwgDQojIHRoZSAnPScgc2lnbiBoYXMgdG8gYmUgImVzY2FwZWQiICAnXFw9Jy4gIA0KIyBUaGUgcHJlY2lzZSByZWdleCBwYXR0ZXJuIHN5bnRheCBoYXMgdGhlIGVxdWFsIHNpZ24gZm9sbG93ZWQgYnkgYSBkaWdpdCAnXFxkJy4NCiMgbXVsdGlwbGUgZGlnaXRzICAnKycsICBpLmUuICdcXGQrJw0KIyBTaW5jZSB0aGUgZGlnaXRzIGFyZSBmb3VuZCBhdCB0aGUgZW5kIG9mIHRoZSB2YWx1ZSBvZiB0aGUgdmFyaWFibGUsIHVzZSBhbiBhbmNob3IgDQojIFRvIGFuY2hvciB0aGUgZW5kIG9mIHRoZSByZWdleCBwYXR0ZXJuLCB1c2UgdGhlICckJyByZWdleCBhbmNob3Igbm90YXRpb24uDQojIFJlbWVtYmVyLCB3ZSBhcmUgbWF0Y2hpbmcgZXZlcnl0aGluZyBiZWZvcmUgdGhlIHBhdHRlcm4uICBNYXRjaCB3aXRoIHRoZSB3aWxkY2FyZDogJy4qJw0KIyBIb25lc3RseSwgdGhlcmUncyBubyBzdWJzdGl0dXRlIGZvciBrbm93aW5nIGEgYml0IGFib3V0IHJlZ2V4IHdoZW4geW91J3JlIGNvZGluZy4NCiMNCiMgU2VlIHRoZSAid29yayB3aXRoIHN0cmluZ3MiIGNoZWF0c2hlZXQuICBodHRwczovL3JzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8NCg0KbmF2X2RmDQpgYGANCg0KDQoNCiMjIEl0ZXJhdGUNCg0KVXNlIGBwdXJycjo6bWFwYCAqKmluc3RlYWQqKiBvZiAqKidmb3InKiogbG9vcHMuICBCZWNhdXNlIHB1cnJyIGlzIHRoZSBSL1RpZHl2ZXJzZSB3YXkuICAnRm9yJyBsb29wcyBhcmUgZmluZSwgYnV0IGludmVzdCBzb21lIHRpbWUgbGVhcm5pbmcgcHVycnIgYW5kIHlvdSdsbCBiZSBiZXR0ZXIgb2ZmLiAgU3RpbGwsIHRoZXJlJ3Mgbm8gd3Jvbmcgd2F5IHRvIGl0ZXJhdGUgYXMgbG9uZyBhcyB5b3UgZ2V0IHRoZSByaWdodCBhbnN3ZXIuICBTbywgZG8gd2hhdCB3b3Jrcy4gIEJlbG93IGlzIHRoZSBUaWR5dmVyc2UvUHVycnIgd2F5Li4uLg0KDQpOb3cgdGhhdCBJIGhhdmUgYSBmdWxsIGxpc3Qgb2YgbmF2aWdhdGlvbiBVUkxzLCBlYWNoIG9mIHdoaWNoIHJlcHJlc2VudHMgYSB3ZWIgcGFnZSB0aGF0IGhhcyBhIHN1bW1hcnkgb2YgNTAgbmFtZXMvbGlua3MuICBNeSBuZXh0IHRhc2sgaXMgdG8gcmVhZCB0aGUgSFRNTCBvZiBlYWNoIFVSTCByZXByZXNlbnRpbmcgYSB0YXJnZXQtbmFtZS4gIEJ5IHJlYWRpbmcgdGhlIFVSTCAoaW1wb3J0aW5nIHRoZSBIVE1MKSBmb3IgZWFjaCB0YXJnZXQgbmFtZSwgSSB3aWxsIHRoZW4gaGF2ZSBIVE1MIGZvciBlYWNoIGluZGl2aWR1YWwgdGFyZ2V0IHBlcnNvbiBpbiB0aGUgZGF0YWJhc2UuICBPZiBjb3Vyc2UsIEkgc3RpbGwsIHRoZW4sIGhhdmUgdG8gcmVhZCBhbmQgcGFyc2UgdGhlIEhUTUwgb2YgdGhvc2UgdGFyZ2V0LW5hbWUgcGFnZXMsIGJ1dCBJIGNhbiBkbyB0aGF0LiAgVGhlIHNjcmFwaW5nIChjcmF3bGluZyArIHBhcnNpbmcpIHdvcmtzIHdoZW4gSSBoYXZlIGEgVVJMIHBlciB0YXJnZXQgcGVyc29uLiAgSGF2aW5nIGEgVVJMIGZvciBlYWNoIHRhcmdldCBwZXJzb24gbWVhbnMgSSBjYW4gc3lzdGVtYXRpY2FsbHkgc2NyYXBlIHRoZSB3ZWIgc2l0ZS4gIEluIG90aGVyIHdvcmRzLCBJIGNyYXdsIHRoZSBzdW1tYXJ5IG5hdmlnYXRpb24gdG8gY29uc3RydWN0IGEgVVJMIGZvciBlYWNoIHN1bW1hcnkgcGFnZS4gIFRoZW4gSSBpbXBvcnQgSFRNTCBmb3IgZWFjaCBzdW1tYXJ5IHBhZ2UgdG8gZ2V0IGEgVVJMIHRvIGVhY2ggcGVyc29uJ3MgcGFnZS4gIFRoZW4gSSBpbXBvcnQgZWFjaCBwZXJzb24ncyBwYWdlIGFuZCBwYXJzZSB0aGUgSFRNTCBmb3IgZWFjaCBwZXJzb24ncyByZWNvcmQuDQoNCkJ1dCwgYmFjayB0byB0aGUgY3VycmVudCB0YXNrOiAgaW1wb3J0IHRoZSBIVE1MIGZvciBlYWNoIHN1bW1hcnkgcmVzdWx0cyBwYWdlIG9mIDUwIHJlY29yZHMuLi4NCg0KKipOb3RlKio6IHRoYXQsIGJlbG93LCBJIGludHJvZHVjZSBhICoqcGF1c2UqKiAoYFN5cy5zbGVlcCgpYCkgaW4gZnJvbnQgb2YgZWFjaCBgcmVhZF9odG1sKClgIGZ1bmN0aW9uLiAgVGhpcyBpcyBhIGNvbW1vbiB0ZWNobmlxdWUgZm9yIHdlbGwgYmVoYXZlZCB3ZWIgc2NyYXBpbmcuICBQYXVzaW5nIGJlZm9yZSBlYWNoIGByZWFkX2h0bWxgIGZ1bmN0aW9uLCBhdm9pZHMgb3ZlcndoZWxtaW5nIG15IHRhcmdldCdzIHNlcnZlci9uZXR3b3JrIGluZnJhc3RydWN0dXJlLiAgSWYgSSBvdmVyd2hlbG0gdGhlIHRhcmdldCBzZXJ2ZXIsIHRoZSBzZXJ2ZXIgaG9zdC1wZW9wbGUgbWF5IGNvbnNpZGVyIG1lIGEgRE5TIGF0dGFjay4gIElmIHRoZXkgdGhpbmsgSSdtIGEgRE5TIGF0dGFja2VyLCB0aGV5IG1pZ2h0IGNob29zZSB0byBibG9jayBteSBjb21wdXRlciBmcm9tIGNyYXdsaW5nIHRoZWlyIHNpdGUuICBJZiB0aGF0IGhhcHBlbnMsIEknbSB1cCBhIGNyZWVrLiAgSSBkb24ndCB3YW50IHRoYXQuICBJIHdhbnQgbXkgc2NyaXB0IHRvIGJlIGEgd2VsbCBiZWhhdmVkIGJvdC1jcmF3bGVyLiAgDQoNClNwZWFraW5nIG9mIGJlaW5nIGEgZ29vZCBhbmQgaG9ub3JhYmxlIHNjcmFwZXItY2l0aXplbiwgZGlkIEkgYnJvd3NlIHRoZSBbcm9ib3RzLnR4dF0oaHR0cDovL3d3dy52b25kZWwuaHVtYW5pdGllcy51dmEubmwvcm9ib3RzLnR4dCkgcGFnZSBmb3IgdGhlIHNpdGU/ICBEaWQgSSBjaGVjayB0aGUgc2l0ZSBmb3IgYSBUZXJtcyBvZiBTZXJ2aWNlIHBhZ2U/ICBEaWQgSSBsb29rIHRvIHNlZSBpZiB0aGVyZSB3ZXJlIGFueSB3cml0dGVuIHByb2hpYml0aW9ucyBhZ2FpbnN0IHdlYiBjcmF3bGluZywgc3lzdGVtYXRpYyBkb3dubG9hZGluZywgY29weXJpZ2h0LCBvciBsaWNlbnNpbmcgcmVzdHJpY3Rpb25zPyAgSSBkaWQgYW5kIHlvdSBzaG91bGQgdG9vLiAgQXMgb2YgdGhpcyB3cml0aW5nLCB0aGVyZSBkbyBub3QgYXBwZWFyIHRvIGJlIGFueSByZXN0cmljdGlvbnMgZm9yIHRoaXMgc2l0ZS4gIFlvdSBzaG91bGQgcGVyZm9ybSB0aGVzZSB0eXBlcyBvZiBnb29kLXNjcmFwaW5nIGh5Z2llbmUgc3RlcHMgZm9yIGV2ZXJ5IHNpdGUgeW91IHdhbnQgdG8gc2NyYXBlIQ0KDQpOb3RlOiAgQmVsb3csIGZvciBkZXZlbG9wbWVudCBwdXJwb3NlcywgSSBsaW1pdGVkIG15IGNyYXdsaW5nIHRvIDMgcmVzdWx0cyBsaW5rczogIGBteV91cmxfZGYkdXJsWzE6M11gLiAgQmUgY29uc2VydmF0aXZlIGR1cmluZyB5b3VyIGNvZGUgZGV2ZWxvcG1lbnQgdG8gYXZvaWQgYXBwZWFyaW5nIGFzIGEgRE5TIGF0dGFja2VyLiAgTGF0ZXIsIHdoZW4geW91IGFyZSByZWFkeSB0byBjcmF3bCB5b3VyIHdob2xlIHRhcmdldCBzaXRlLCB5b3UnbGwgd2FudCB0byByZW1vdmUgc3VjaCBsaW1pdHMgKGkuZS4gYFsxOjNdYC4pICBCdXQgZm9yIG5vdywgZG8gZXZlcnlvbmUgYSBmYXZvciBhbmQgdHJ5IG5vdCB0byBiZSBvdmVyIGNvbmZpZGVudC4gIFN0YXkgaW4gdGhlIGtpZGRpZSBwb29sLiAgRG8geW91ciBkZXZlbG9wbWVudCB3b3JrIHVudGlsIHlvdSBhcmUgc3VyZSB5b3UncmUgbm90IGFjY2lkZW50YWxseSB1bmxlYXNoaW5nIGEgbWFsaWNpb3VzIG9yIHBvb3JseSBjb25zdHJ1Y3RlZCB3ZWIgY3Jhd2xlci4NCg0KTm90ZTogIEJlbG93LCBJIGFtIGtlZXBpbmcgdGhlIG9yaWdpbmFsIHRhcmdldCBVUkwgdmFyaWFibGUsIGBzdW1tYXJ5X3VybGAsIGZvciBsYXRlciByZWZlcmVuY2UuICBUaGlzIHdheSBJIHdpbGwgaGF2ZSBhIHJlY29yZCBvZiB3aGljaCBwYXJzZWQgZGF0YSByZXN1bHRzIGNhbWUgZnJvbSB3aGljaCBVUkwgd2ViIHBhZ2UuICANCg0KTm90ZTogIEJlbG93LCB0aGUgZmluYWwgcmVzdWx0IGlzIGEgdGliYmxlIHdpdGggYSB2ZWN0b3IsIGBzdW1tYXJ5X3VybGAsIGFuZCBhbiBhc3NvY2lhdGVkIGNvbHVtbiBvZiBIVE1MIHJlc3VsdHMsIGVhY2ggcmVzdWx0IGlzIHN0b3JlZCBhcyBhIG5lc3RlZCBSIF9saXN0Xy4gIFRoYXQgaXMsIGEgY29sdW1uIG9mIGRhdGEgdHlwZXMgdGhhdCBhcmUgYWxsICJfbGlzdHNfIiwgYWthIGEgIl9saXN0IGNvbHVtbl8iLiAgUGVyc29uYWxseSBJIGZpbmQgbGlzdHMgdG8gYmUgYSBwYWluLiBJIHByZWZlciB3b3JraW5nIHdpdGggdGliYmxlcyAoYWthIF9kYXRhIGZyYW1lc18uKS4gIEJ1dCBfbGlzdHNfIGFwcGVhciBvZnRlbiBpbiBSIGRhdGEgd3JhbmdsaW5nLCBlc3BlY2lhbGx5IHdoZW4gc2NyYXBpbmcgd2l0aCBgcnZlc3RgLiAgVGhlIG1vcmUgeW91IHdvcmsgd2l0aCBfbGlzdHNfLCB0aGUgbW9yZSB5b3UgY29tZSB0byB0b2xlcmF0ZSBfbGlzdHNfIGZvciB0aGUgZmxleGlibGUgZGF0YSB0eXBlIHRoYXQgdGhleSBhcmUuICBBbnl3YXksIGlmIEkgd2VyZSB0byBsb29rIGF0IG9ubHkgdGhlIGZpcnN0IHJvdyBvZiByZXN1bHRzIGZyb20gdGhlIGh0bWxfcmVzdWx0cyBjb2x1bW4sIGBuYXZfcmVzdWx0c19saXN0JGh0bWxfcmVzdWx0c1sxXWAsIEkgd291bGQgZmluZCBhIF9saXN0XyBvZiB0aGUgcmF3IEhUTUwgZnJvbSB0aGUgZmlyc3Qgc3VtbWFyeSByZXN1bHRzIHBhZ2UgaW1wb3J0ZWQgdmlhIGByZWFkX2h0bWwoKWAuICANCg0KUmVjYXBwaW5nOiAgVGhpcyBpcyB0ZXN0aW5nLiBJIGhhdmUgdGhyZWUgVVJMcyBgKGh0bWxfcmV1bHRzWzE6M10pYCwgb25lIGZvciBlYWNoIG9mIHRoZSBmaXJzdCB0aHJlZSBuYXZpZ2F0aW9uIHN1bW1hcnkgcGFnZXMuICBFYWNoIHN1bW1hcnkgcGFnZSB3aWxsIGNvbnRhaW4gdGhlIHJhdyBIVE1MIGZvciA1MCBuYW1lcy4gSSB3aWxsIGByZWFkX2h0bWxgIGVhY2ggbGluaywgd2FpdGluZyAyIHNlY29uZHMgYmV0d2VlbiBlYWNoIGByZWFkX2h0bWxgLiAgDQoNCmBgYHtyfQ0KbmF2X3Jlc3VsdHNfbGlzdCA8LSB0aWJibGUoDQogIGh0bWxfcmVzdWx0cyA9IG1hcChuYXZfZGYkdXJsWzE6M10sDQogICAgfiB7DQogICAgICAjdXJsWzE6M10gLSBsaW1pdGluZyB0byB0aGUgZmlyc3QgdGhyZWUgc3VtbWFyeSByZXN1bHRzIHBhZ2VzIChlYWNoIHBhZ2UgPSA1MCByZXN1bHRzKQ0KICAgICAgU3lzLnNsZWVwKDIpDQogICAgICAjIERPIFRISVMhICBzbGVlcCAyIHdpbGwgcGF1c2UgMiBzZWNvbmRzIGJldHdlZW4gc2VydmVyIHJlcXVlc3RzIHRvIGF2b2lkIGJlaW5nIGlkZW50aWZpZWQgYW5kIHBvdGVudGlhbGx5IGJsb2NrZWQgYnkgbXkgdGFyZ2V0IHdlYiBzZXJ2ZXIgdGhhdCBtaWdodCBzZWUgbXkgY3Jhd2xpbmcgYm90IGFzIGEgRE5TIGF0dGFjay4NCiAgICAgIC54ICU+JQ0KICAgICAgICByZWFkX2h0bWwoKQ0KICAgIH0pLA0KICBzdW1tYXJ5X3VybCA9IG5hdl9kZiR1cmxbMTozXQ0KKQ0KDQpuYXZfcmVzdWx0c19saXN0DQpgYGANCg0KTm93IEkgaGF2ZSB0aHJlZSByb3dzIG9mIF9saXN0c18sIGVhY2ggbGlzdCB3aXRoIDUwIGxpbmtzLCBpbiBhIHRpYmJsZS4gIEVhY2ggbGluayBsZWFkcyB0byBhIHRhcmdldCBuYW1lIHRoYXQgSSBjYW4gZXZlbnR1YWxseSBgcmVhZF9odG1sYCB0byBnYXRoZXIgdGhlIHJhdyBIVE1MIG9mIHRoYXQgdGFyZ2V0IG5hbWUuIA0KDQpCdXQgZmlyc3QsIEkgd2FudCB0byBleHBhbmQgdGhlIHRocmVlIGxpc3RzIHNvIEkgaGF2ZSBhIHNpbmdsZSB0aWJibGUgb2YgMTUwIFVSTHMgdG8gdGFyZ2V0IG5hbWVzLiAgVXNpbmcgcHVycnIgKGBtYXAoKWApLCBJIGNhbiBpdGVyYXRlIG92ZXIgdGhlIHJlc3VsdHMgX2xpc3RzXywgcGFyc2luZyB0aGUgSFRNTCBub2RlcyB3aXRoIGBodG1sX2F0dHIoKWAgYW5kIGBodG1sX3RleHQoKWAuICBJdCBpcyBjb252ZW5pZW50IHRvIGtlZXAgdGhpcyBwYXJzZWQgZGF0YSBpbiBhIHRpYmJsZS4gIFRoZSByZXN1bHRzIHdpbGwgYmUgbmVzdGVkIGxpc3RzIHdpdGhpbiBhIHRpYmJsZS4gIFdoZW4gSSBleHBhbmQgdGhlIG5lc3RlZCBfbGlzdF8gd2l0aCB0aGUgYHVubmVzdCgpYCBmdW5jdGlvbiwgSSB0aGVuIGhhdmUgYSBzaW5nbGUgdGliYmxlIHdpdGggMTUwIFVSTHMgYW5kIDE1MCBuYW1lcywgb25lIHJvdyBmb3IgZWFjaCB0YXJnZXQgbmFtZS4gIA0KDQpgYGB7cn0NCnJlc3VsdHNfYnlfcGFnZSA8LSB0aWJibGUoc3VtbWFyeV91cmwgPSBuYXZfcmVzdWx0c19saXN0JHN1bW1hcnlfdXJsLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgdXJsID0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXAobmF2X3Jlc3VsdHNfbGlzdCRodG1sX3Jlc3VsdHMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH4gLnggJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaHRtbF9ub2RlcygidWwgbGkgYSIpICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGh0bWxfYXR0cigiaHJlZiIpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFwKG5hdl9yZXN1bHRzX2xpc3QkaHRtbF9yZXN1bHRzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB+IC54ICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGh0bWxfbm9kZXMoInVsIGxpIGEiKSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBodG1sX3RleHQoKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQogICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KcmVzdWx0c19ieV9wYWdlDQoNCnJlc3VsdHNfYnlfcGFnZSAlPiUgDQogIHVubmVzdChjb2xzID0gYyh1cmwsIG5hbWUpKSAlPiUgDQogIGZpbHRlcighc3RyX2RldGVjdChuYW1lLCAiRUNBUlRJQ08iKSkgJT4lIA0KICBmaWx0ZXIoIXN0cl9kZXRlY3QobmFtZSwgIl5cXCsiKSkNCiAgDQpgYGANCg0KTm93IEkgY2FuIGl0ZXJhdGUgb3ZlciBlYWNoIG9uZSBvZiB0aGUgVVJMcyB0byB0aGUgdGFyZ2V0IG5hbWVzLiAgVGhlbiBJIGNhbiBwYXJzZSB0aGUgcmF3IEhUTUwgZm9yIGVhY2ggdGFyZ2V0IG5hbWUgcGFnZS4gV2hlbiBJIGZvbGxvdyB0aGUgbGlua3MgZm9yIGVhY2ggbmFtZSwgSSBoYXZlIHRoZSByYXcgSFRNTCBvZiBlYWNoIHBlcnNvbiwgaW4gX2xpc3RzXywgcmVhZHkgdG8gYmUgcGFyc2VkIHdpdGggdGhlIGBodG1sX25vZGVzYCwgYGh0bWxfdGV4dGAsIGFuZCBgaHRtbF9hdHRyYCBmdW5jdGlvbnMuDQoNCiMjIFBhcnNpbmcgZXhhbXBsZSBmb3IgYW4gaW5kaXZpZHVhbCANCg0KTm93IHlvdSBrbm93IGhvdyB0byBnZXQgYSBVUkwgZm9yIGVhY2ggbmFtZSBpbiB0aGUgdGFyZ2V0IGRhdGFiYXNlLiAgVGhhdCBpcywgeW91IGNhbiBjcmF3bCB0aGUgdGFyZ2V0IHNpdGUncyBuYXZpZ2F0aW9uLiAgVGhlIG5leHQgZ29hbCBpcyB0byBpbXBvcnQgYW5kIHBhcnNlIHRoZSBIVE1MIGZvciBlYWNoIF9uYW1lXy4gIEluIG90aGVyIHdvcmRzLCBpbiBteSBkZXZlbG9wbWVudCB0aWJibGUsIEkgc3RpbGwgbmVlZCB0byBjcmF3bCB0aGUgaW5kaXZpZHVhbCB0YXJnZXQgbmFtZXMsIGFsbCAxNTAgbmFtZXMsIDUwIG5hbWVzIHBlciBzdW1tYXJ5IHBhZ2UgZm9yIGVhY2ggb2YgdGhlIDMgZGV2ZWxvcG1lbnQgcGFnZXMuICBCZWxvdyBpcyBhbiBleGFtcGxlIG9mIGdhdGhlcmluZyBhbmQgcGFyc2luZyBpbmZvcm1hdGlvbiBmb3Igb25lIFVSTCByZXByZXNlbnRpbmcgb25lIHBlcnNvbi4gIFRoZSBpbmZvcm1hdGlvbiBnYXRoZXJlZCBpcyBpbmZvcm1hdGlvbiBmcm9tIHRoZSBkZXRhaWxlZCBuYW1lcyBwYWdlIGFib3V0IHRoZSBjaGlsZHJlbiBvZiBvbmUgcGVyc29uIGluIHRoZSB0YXJnZXQgZGF0YWJhc2UuDQoNCmBgYHtyfQ0KIyBodHRwOi8vd3d3LnZvbmRlbC5odW1hbml0aWVzLnV2YS5ubC9lY2FydGljby9wZXJzb25zLzEwNTc5DQojIHNjaGVtYTpjaGlsZHJlbg0KDQplbWFudWVsIDwtICByZWFkX2h0bWwoImh0dHA6Ly93d3cudm9uZGVsLmh1bWFuaXRpZXMudXZhLm5sL2VjYXJ0aWNvL3BlcnNvbnMvMTA1NzkiKQ0KDQpjaGlsZF9maWx0ZXIgPC0gZW1hbnVlbCAlPiUgDQogIGh0bWxfbm9kZXMoInVsIGxpIGEiKSAlPiUgDQogIGh0bWxfYXR0cigicmVsIikgJT4lIA0KICBhc190aWJibGUoKSAlPiUgDQogIG11dGF0ZShpZCA9IHJvd19udW1iZXIoKSkgJT4lIA0KICBmaWx0ZXIoc3RyX2RldGVjdCh2YWx1ZSwgImNoaWxkcmVuIikpDQpjaGlsZF9maWx0ZXINCg0KY2hpbGRfdGV4dCA8LSBlbWFudWVsICAlPiUgDQogIGh0bWxfbm9kZXMoInVsIGxpIGEiKSAlPiUgDQogIGh0bWxfdGV4dCgpICU+JSANCiAgYXNfdGliYmxlKCkgJT4lIA0KICByZW5hbWUodGV4dCA9IHZhbHVlKSAlPiUgDQogIG11dGF0ZShpZCA9IHJvd19udW1iZXIoKSkgJT4lIA0KICBpbm5lcl9qb2luKGNoaWxkX2ZpbHRlcikNCg0KY2hpbGRfdGV4dCAlPiUgDQogIHB1bGwodGV4dCkNCg0KDQpgYGANCg0KRG9uJ3QgZm9yZ2V0IHRvIHVzZSBhIHBhdXNlIGBTeXMuc2xlZXAoKWAgYmV0d2VlbiBlYWNoIHN5c3RlbWF0aWMgaXRlcmF0aW9uIG9mIHRoZSBgcmVhZF9odG1sKClgIGZ1bmN0aW9uLg0KDQojIyBSZXNvdXJjZXMNCg0KLSBodHRwczovL3J2ZXN0LnRpZHl2ZXJzZS5vcmcvDQotIGh0dHBzOi8vY29tbXVuaXR5LnJzdHVkaW8uY29tL3QvbG9vcC1mb3Itd2l0aC1ydmVzdC1mb3Itc2NyYXBpbmcvNTYxMzMvNCAobG9vcGluZyB3aXRoIFJWRVNUKQ0KLSBwdXJyciAvIG1hcCA6OiAgaHR0cHM6Ly9qZW5ueWJjLmdpdGh1Yi5pby9wdXJyci10dXRvcmlhbC9sczAxX21hcC1uYW1lLXBvc2l0aW9uLXNob3J0Y3V0cy5odG1sDQoNCi0tLQ0KPGNlbnRlcj4NCioqSm9obiBMaXR0bGUqKg0KDQoqKkRhdGEgU2NpZW5jZSBMaWJyYXJpYW4qKiAgDQpDZW50ZXIgZm9yIERhdGEgJiBWaXN1YWxpemF0aW9uIFNjaWVuY2VzICANCkR1a2UgVW5pdmVyc2l0eSBMaWJyYXJpZXMgIA0KDQpodHRwczovL0pvaG5MaXR0bGUuaW5mbyAgDQpodHRwczovL1JmdW4ubGlicmFyeS5kdWtlLmVkdSAgDQpodHRwczovL2xpYnJhcnkuZHVrZS5lZHUvZGF0YSAgDQoNCjxpIGNsYXNzPSJmYWIgZmEtY3JlYXRpdmUtY29tbW9ucyBmYS0yeCI+PC9pPiAmbmJzcDsgPGkgY2xhc3M9ImZhYiBmYS1jcmVhdGl2ZS1jb21tb25zLWJ5IGZhLTJ4Ij48L2k+PGkgY2xhc3M9ImZhYiBmYS1jcmVhdGl2ZS1jb21tb25zLW5jIGZhLTJ4Ij48L2k+ICANCkNyZWF0aXZlIENvbW1vbnM6ICBBdHRyaWJ1dGlvbi1Ob25Db21tZXJjaWFsIDQuMCAgDQpodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMvNC4wDQo8L2NlbnRlcj4NCg0KJm5ic3A7ICA=